Copyright: Delphi  v.3.0 Copyright (c) 1982-1997 Borland International, Inc.


 Welcome to Delphi Help

You will see this screen while Delphi is searching for your topic. Wait a few moments.  If this screen is not replaced by your topic, then the topic may be inaccessible using your current search method. To determine if a topic is missing, try using one of the alternative search methods described below. If you do not find the topic, it may be missing from the help files. Visit the Borland web site at www.borland.com to see if updated help files are available.

You can search for topics in several ways:

1	Press F1 on a property or event name in the Object Inspector.

2	Press F1 on a property, event, method, function, procedure, or type in the Code editor.

3	Click the Contents tab to browse through topics by category.

4	Click the Index tab to see a list of index entries.

5	Click the Find tab to search for words or phrases


Response Editor dialog box

See Also

This dialog box lets you to define the contents and format of an HTTP response message for the TQueryTableProducer or TDataSetTableProducer component.

To display the Response Editor dialog box, place a query table producer or dataset table producer on a form. Right-click the component and choose Response Editor from the menu.

Dialog box options

Field list

The field list in the upper center of this dialog box shows the Field Name and Field Type for each field in the response table. You can use the Object Inspector to change properties for the highlighted field.

Table Properties

These are the properties of the overall response table.

Align

THTMLTableAttributes.Align, the horizontal alignment of the table within the HTML document.

Border

THTMLTableAttributes.Border, the width of the table border; -1 indicates no border will be drawn.

BgColor

THTMLTableAttributes.BgColor, the background color of the HTML table.

Cellpadding

THTMLTableAttributes.CellPadding, the amount of space to leave around the contents of each cell in the HTML table. A value of -1 indicates that the web browser should decide how to pad the cells of the HTML table.

Cellspacing

THTMLTableAttributes.CellSpacing, the amount of space to leave between cells in the HTML table. A value of -1 indicates that the web browser should decide how to separate the cells of the HTML table.

Width

THTMLTableAttributes.Width, the width of the entire HTML table as a percentage of the width of the web browser window. The default, 100, means the table will span the entire browser window.

Add button

Adds a column to the response table. Use the Object Inspector to enter the title and choose a field.

Delete button

Removes the selected column from the table.

Move Up/Down buttons

Change the order of columns in the table by moving the selected column up (toward the left edge of the table) and down (toward the right).

Add All Fields button

Creates a column for every field in the dataset (query) bound to the query table producer.

Restore Defaults

Restores default property settings for the selected column.


About forms

See Also

Forms are the foundation of all Delphi applications. The form is a component. You place other components onto the form to build an application interface.

You develop your application by customizing the main form, and adding and customizing forms for other parts of the interface. You customize forms by adding components and setting properties.

The form is a window, and therefore by default includes standard window functionality such as:

Control menu

Minimize and Maximize buttons

Title bar

Resizeable borders

You can change these features, as well as any other property of the form, at design time using the Object Inspector.


See also

Adding components to the form

Developing custom dialog boxes

Creating a user interface

Setting component properties

TForm component

Using the Form component


Client area

Enables you to view or modify any part of a form in the active window. Use the grid of dots to align objects on the form.


About the SpeedBar

See Also

The Delphi SpeedBar provides shortcuts for menu commands. The graphic below shows the default SpeedBar. However, you can customize the SpeedBar by choosing Properties from the SpeedBar SpeedMenu.

To find out more about a default SpeedBar button, click a button in the graphic above.

You can use the separator line that lies between the SpeedBar and Component palette to horizontally resize the SpeedBar.

The SpeedBar has Help Hints. To enable Help Hints, select Show Hints from the SpeedBar SpeedMenu.


See also

Component palette

Configuring the SpeedBar

Keyboard shortcuts


Open Project button

Displays the Open Project dialog box so you can open a new project.

The menu equivalent is File|New.


Open File button

Displays the Open File dialog box so you can open a new text file.

The menu equivalent is File|Open.


Save button

Stores changes made to all files in the open project the current name of each file.

If you have not previously saved the project, Delphi opens the Save As dialog box, where you can enter a file name.


Save button

Stores changes made to active file in the Code Editor.

If you have not previously saved the file Delphi opens the Save As dialog box where you can enter a filename.

The menu equivalent is File|Save.


Add File To Project button

Adds the active file in the Code Editor to the currently open project.

The menu equivalents are:

File|Add to Project

Add File on the Project Manager SpeedMenu


Remove File From Project button

Opens the Remove From Project dialog box, where you can select a file that you want to remove from the open project.

The menu equivalents are:

File|Remove from Project

Remove File on the Project Manager SpeedMenu


Select Unit From List button

Displays the View Unit dialog box, where you can view any unit in the current project.

When you choose a unit, that unit becomes the active page in the Code Editor.

The menu equivalent is View|Units.


Select Form From List button

Displays the View Form dialog box, where you to view any form in the current project.

When you choose a form, it becomes the active form.

The menu equivalent is View|Forms.


Toggle Form/Unit button

Makes the Code Editor the active window when the form is selected and makes the form the active window when the Code Editor is selected.

The menu equivalent is View|Toggle Form/Unit.


New Form button

Creates a blank form and a new unit and adds them to the project.

The menu equivalent is File|New.


Run button

Compiles and executes your application.

The menu equivalent is Run|Run.


Pause button

Pauses program execution and positions the execution point on the next line of code to execute.


Trace Into button

Executes the program statement highlighted by the execution point. Trace Into lets you execute routines, written by the programmer, one statement at a time. When the routine returns from the call the debugger positions the execution point on the statement following the routine call.

If the execution point highlights a routine whose code was generated by Delphi, choosing Trace Into causes the debugger to position the execution point at the statement following the routine call.

Note:	The Trace Into button is disabled if symbolic debug information is off.

The menu equivalent is Run|Trace Into.


Step Over button

Executes your program one statement at a time, without branching into subroutines. Stepping through your program is helpful if your program is about to execute a routine whose code you do not need to debug at this time.

If the execution point is located on a call to a routine, then issuing Step Over runs that routine at full speed and places the execution point on the statement following the routine call.

Note:	The Step Over button is disabled if symbolic debug information is off.

The menu equivalent is Run|Step Over.


Opening a SpeedMenu

Right-click in the window.

Press Alt+F10 when the cursor is in the window.


About the Code Editor

See Also

The Code Editor is a full-featured editor. It gives you access to the code that runs your application and offers many powerful features such as:

Brief-style editing

Color syntax highlighting

Multiple and group Undo

A full range of editing commands

Code Insight tools

Many commands are available on the Code Editor SpeedMenu.

To customize the Code Editor, use the Tools | Environment Options  dialog box.

To get get Help on a token in the Code Editor, place your cursor on that token and pressing F1.

When you open a new project, Delphi adds a page in the Code Editor for the main Form unit.

At compile time, if you receive an error, Delphi does the following things:

Displays the error in the Code Editor message box

Highlights the offending line


Viewing different files in the Code Editor

Click its associated tab.


See also

Behind the scenes in the Code Editor

Code basics

Error messages

Object Inspector

Project Manager

Working with the Code editor

Viewing different files in the Code Editor

Working with event handlers


Maximize button

Grows your window to encompass your entire screen.


Code Editor

Enables you to view or modify any part of the source code contained in the active page.


Page tabs

Provides a way to move between the open files in the Code Editor.


Title bar

Displays the name of the active file in the Code Editor.


Line and column indicator

Displays the line and column position of the cursor in the Code Editor. The first and second numbers show the line number and column number, respectively.


Modified indicator

Indicates whether the text in the active page of the Code Editor has been modified since the last time the file was saved. (Blank if the file has not been modified.)


Mode indicator

Indicates whether the editor is in Insert or Overwrite mode.

In Insert mode (the default mode), text you type is inserted at the cursor.

In Overwrite mode, text you type overwrites previously entered text.

Use the Insert key on your keyboard to toggle between these two modes.


About the Menu Designer

The Delphi Menu Designer enables you to easily add menus to your form. You can simply add menu items directly into the Menu Designer window. You can add, delete, and rearrange menu items at design time and you do not have to run the program to see the results. Your applications menus are always visible on the Form, as they will appear during run time.

You can build each menu structure entirely from scratch, or you can start from one of the Delphi Menu templates (predesigned menus).

You can also dynamically change menus, to provide more information or options to the user.

For more information about the Menu Designer, see the following topics:

Opening the Menu Designer

Editing Menu Items Without Opening the Menu Designer

Menu Designer SpeedMenu

Using Menu Templates

Importing Menus from Resource Files

Accessing and Editing Menus at Run Time


Menu title area

Menu titles display in this area. Click the highlighted block to add new items to the menu.


Menu command area

Menu commands display in this area. Click the highlighted block to add new menu commands


About the Object Inspector

See also

The Delphi Object Inspector is the gateway between your application's visual appearance and the code that makes your application run.

The Object Inspector enables you to:

Set design-time properties for components you have placed on a form (or for the form itself), and

Create and help you navigate through event handlers.

The Object selector at the top of the Object Inspector is a drop-down list containing all the components on the active form and it also displays their object type. This lets quickly select different components on the current form..

You can resize the columns of the Object Inspector by dragging the separator line to a new position.

The Object Inspector has two pages:

Properties page

Events page


See also

Setting component properties

Code basics


About the Object Inspector: Properties page

See Also

The Properties page of the Object Inspector enables you to set design-time properties for components on your form, and for the form itself. You can set run-time properties by writing source code inside event handlers.

The Properties page displays only the properties of the component that is selected on the form.

By setting properties at design time you are defining the initial state of a component.


See also

Events page

Using property editors

Setting component properties

Viewing nested properties


About the Object Inspector: Events page

See Also

The Events page of the Object Inspector enables you to connect forms and components to program events. When you double-click an event from the Events page, Delphi creates an event-handler and switches focus to the Code Editor. In the Code Editor, you write the code inside event-handlers that specifies how a component or form responds to the a particular event.

The Events page displays only the events of the component that is selected in the form.


See also

Displaying and coding shared events

Locating an event handler that's not the default

Properties page

Using property editors


Object selector

Displays the active component whose properties and events you are currently editing. You can use the drop-down list to select a component.


Property column

Lists the design-time properties for the component you have selected on your form.


Value column

Displays the current value for a property. Each value uses one of the Delphi property editors to set values.


Object Inspector tabs

Provide you with a means to switch between the Property page and the Event page of the Object Inspector. To change pages, click a tab.


Minimize button

Shrinks the window down to an icon.


Scroll bars

Provides you with a means to scroll the current window to view objects that cannot fit into the window. To scroll a window, click the up scroll arrow or the down scroll arrow, or you can drag the scroll box.


Control menu

Click the Control-menu box to open the Control menu for the current window.


Handler column

Displays the event-handler link for an event. To generate a default event-handler link for an event, double-click the Handler column.


Event column

Lists the possible events for the component you have selected on your form.


About the Project Manager

See Also

The Delphi Project Manager displays the form and unit files associated with your project. These files are listed in the uses clause of your .DPR file. The Project Manager also enables you to easily navigate between files while you are developing your application project.

The Project Manager also has a SpeedMenu, which you can display by right-clicking anywhere inside the window area.

When you start Delphi, you can open the Project Manager by choosing View|Project Manager. If you save your desktop settings, you can have the Project Manager window opened by default when you open any project.

If you share files among different projects, using the Project Manager is recommended because you can quickly and easily see the location of each file in the project. This is especially helpful to know when creating backups that include all files the project uses.


Opening the Project Manager

Choose View|Project Manager.

Note:	You must have a project open in order to view the Project Manager.


Closing the Project Manager

Double-click the Control-menu box.

Choose Close from the Control menu.


See also

Project Manager SpeedMenu

Compiling, building, and running projects

Working with projects

Setting project options

Working with projects

Backups


Add File button

Opens the Add To Project dialog box, where you can choose a unit (.PAS) or form file (.DFM) to include in the project.

Note:	You can open other files from this dialog box, but they will not be added to the project.


Remove File button

Deletes any files currently selected in the Project Manager from the project.

Note:	Does not delete the file from the project directory.


View Unit button

Displays the selected file in Code Editor. If the file is not currently open, Delphi will open it.


View Form button

Displays the form for the selected module. This button is dimmed if no form is associated with the selected unit.


Options button

Opens the Project|Options dialog box, where you can specify compiler directives, linker options, output directories, conditional defines, and files to be included with the project.


Update button

Synchronizes the files listed in the Project Manager with the contents of the project file. This button is disabled unless you manually modified the .DPR file.


Unit Name column

Double-click a filename in this column to view the source for the selected unit. If the unit you selected is not currently open, Delphi will open it.


Form Name column

Double-click a filename in this column to view the selected form. If the form you selected is not currently open, Delphi will open it.

If this column is empty, there is no form associated with the unit listed in the Unit Name column.


File Status line

Displays the DOS file name of the project file and indicates the number of forms and units included.


Path Name column

Display the path name of the form and unit file. If the unit has not been saved this column is empty.


Designing Menus

The Delphi Menu Designer enables you to easily add a menu, either predesigned or custom tailored, to your form. You simply add a menu component to the form, open the Menu Designer, and type menu items directly into the Menu Designer window. You can add or delete menu items, or drag and drop them to rearrange them during design time.

You don't even need to run your program to see the results; your design is immediately visible in the form, appearing just as it will during run time.

Your code can also change menus at run time, to provide more information or options to the user. For information, see Accessing and Editing Menus at Run Time..


ObjectBrowser window
The ObjectBrowser window

See also

The ObjectBrowser enables you to visually step through the object hierarchies, units, and global symbols of your application. You can use the ObjectBrowser to:

Graphically view the object hierarchies in your application

List the units your program uses, and view the symbols declared in the interface part or implementation part based on your compiler settings

List all global symbols in your application, view their declarations, and go to references in your source code

The ObjectBrowser has a SpeedMenu that enables you to change symbols you are browsing and to choose display options. To access the SpeedMenu, right-click anywhere in the ObjectBrowser window or press Alt+F10.

The ObjectBrowser has two resizable panes, the Inspector pane and the Details pane.

To find out more about any part of the ObjectBrowser window, click an area in the graphic above.

To resize the Inspector pane or the Details pane:

1.	Place the cursor between the two panes until the cursor changes to a double-headed arrow.

2.	Drag the separator to the left or to the right.

For more information about using the ObjectBrowser, choose one of the following topics:

Browsing symbols

Searching for symbols in the ObjectBrowser

Setting filters

Enabling the ObjectBrowser


See also

Inheritance tree

ObjectBrowser keyboard shortcuts

Options|Environment|Browser


Inspector pane

Displays the symbols for the area that you are browsing. You can select symbols in this pane to inspect the declarations, inheritance information, or references for that symbol.

When you first open the ObjectBrowser, you are browsing objects, and the Inspector pane displays the object inheritance tree. You can change this default on the Browser page of the Environment Options dialog box. Open the Environment Options box from the Tolls menu.

To change what you are browsing in the Inspector:

Choose Objects, Units, Globals, or Symbol from the ObjectBrowser SpeedMenu.


Details pane

Provides detailed information about the object, unit, global, or symbol selected in the Inspector pane. There are three pages in the Details pane:

Scope page

Lists all the symbols declared in the object, unit, global, or symbol selected in the Inspector pane. Change the display by selecting either Sort Always or Qualified Symbols from the ObjectBrowser SpeedMenu.

Inheritance page

Displays a collapsible local tree of ancestor and descendant objects for the object selected in the Inspector pane.

Reference page

Lists all the file names and line numbers where the symbol you have selected in the Inspector pane appears in your code. If there is no reference information for a symbol, that symbol either was not used in your code or was contained in a unit that was compiled with Symbol Info off. You can double-click any reference to jump to that line in your code.


Show Buttons

Toggles the display of the ObjectBrowser filters, which enable you to limit the symbols you are browsing. To add or remove a filter, click it.

If you selected Show Hints from the ObjectBrowser SpeedMenu, Delphi displays the name of the button if you leave the mouse over the button for longer than one second.


Info Line

Displays the symbol type that you are currently browsing.

You can toggle the Info Line display by choosing Info Line from the ObjectBrowser SpeedMenu.


History list

Click History to open the ObjectBrowser History dialog box, which displays a list of symbols you previously browsed. The most recently browsed symbol is listed first.

To return to a previous view, double-click its name.


Back button

Click Back to rewind to the previous ObjectBrowser view. If you step back, the ObjectBrowser loses all the information for what you were just browsing.


Enabling the ObjectBrowser

See also

The ObjectBrowser provides a means for you to search your code for different symbols.

To open the ObjectBrowser:

Choose View|Browser.

Before you can start the ObjectBrowser, you need to compile your program. If you do not know how to compile your program, see Compiling, building, and running projects

Whenever you compile a project, you can always use the ObjectBrowser to inspect symbols that were declared in the interface part of the module. However, you can add more information by enabling the Debugging options on the Compiler page of the Project Options dialog box. The following table lists the Debugging options and their effect on the ObjectBrowser.

Option	When checked

Debug Information	Adds those symbols declared in the implementation part of the module.

Local Symbols	Adds line number references that you can view on the References page of the Details pane.

Symbol Info	Adds all the identifiers that are declared within a routine. That is, symbols are local in scope to a particular routine.

Note:	Debug Information must be checked in order for the Local Symbols and Symbol Info options to have any effect.


See also

The ObjectBrowser window

Browsing symbols

ObjectBrowser keyboard shortcuts

Searching for symbols in the browser

Setting filters


Browsing symbols

See also

When you are viewing symbols using the ObjectBrowser, you might want to further inspect them by viewing its declaration, its object hierarchy, or implementation in the source code.

To inspect a symbol from the Inspector pane:

Select a symbol in the Inspector pane. The contents of the Details pane are updated to reflect the contents of the selected symbol.

Objects, units, globals, and symbols that have their own declarations and can be further inspected, based on the Debugging options on the Compiler page of the Project Options dialog box.

To further inspect a symbol from the Inspector pane:

Double-click the symbol in the Inspector pane.

After you select the symbol, the previous contents of the Details pane move to the Inspector pane, and the declarations from the selected symbol are now listed in the Details pane. You can then inspect those symbols.

To view the symbol you are inspecting in your code, do one of the following:

Select a symbol to browse and press Ctrl+Enter. The Code Editor becomes the active window, and Delphi places the cursor at the point of reference.

Select a symbol to browse and press the Spacebar to activate source code highlighting. After pressing the Spacebar, you can arrow through the symbols in the active pane to view the symbol reference in the source code. When you encounter a symbol that is not used in the active unit, this command becomes disabled.

Pressing the Spacebar does not transfer focus from the ObjectBrowser to the Code Editor.

When the selected symbol is a function or procedure, inspecting the code displays the implementation declaration of the routine. When the selected symbol is a constant, property, variable, or type, inspecting the code displays the symbol's declaration.


See also

Enabling the ObjectBrowser

The ObjectBrowser window

ObjectBrowser keyboard shortcuts

Searching for symbols in the ObjectBrowser

Setting filters


Searching for symbols in the ObjectBrowser

See also

ObjectBrowser uses an incremental search in both the Inspector pane and the Details pane. That is, if you want to search for a certain routine, you can type in its name, and the focus of the ObjectBrowser shifts to the symbol that most closely matches what you just typed.

As you type, the ObjectBrowser displays the characters between the filter buttons and the History button. To cancel the current search, press Esc.

Note:	The Object Browser does not accept incremental search characters that do not match a symbol in the active pane.

To clear the characters in the incremental search, Press Esc.

To find the next match, Press Ctrl+N.


See also

Browsing symbols

Enabling the ObjectBrowser

The ObjectBrowser window

ObjectBrowser keyboard shortcuts

Setting filters


Inheritance tree

See also

When you are browsing objects using the ObjectBrowser, the Inspector pane displays an inheritance tree. The inheritance tree is an outline detailing the ancestor/descendant relationship for all the objects used in your application. Objects connected to each other by branches on the tree are inherited from a common base object.

The inheritance tree is also used on the Inheritance page of the Details pane.

Each object node on the inheritance tree has an outline indicator. The outline indicator displays whether or not that node can be expanded.

Indicator	Description

Node of the tree is collapsed and inherited objects are not listed.

Node of the tree is expanded and inherited objects are listed.

No objects are inherited from this object.

To expand or collapse a node of the inheritance tree:

Click the outline indicator to the left of the node.


See also

The ObjectBrowser window


ObjectBrowser keyboard shortcuts

You can use the following keys to move through the ObjectBrowser:

Key	Action

Up Arrow	Move up one symbol

Down Arrow	Move down one symbol

Ctrl+Up Arrow	Scroll up through the current pane without changing your selected symbol

Ctrl+Down Arrow	Scroll down through the current pane without changing your selected symbol

Ctrl+PgUp	Move to the top of the current pane without scrolling

Ctrl+PgDown	Move to the bottom of the current pane without scrolling

Home	Move to the top of the current pane

End	Move to the bottom of the current pane

Ctrl+Enter	Set focus on the implementation or declaration of the selected symbol

Spacebar	Highlight the source code for the selected symbol, without closing the ObjectBrowser or transferring focus

Enter	Browse the currently selected symbol

+	Expand the current node

-	Collapse the current node

*	Expand the entire tree

PgUp	Move up one screen in the current pane

PgDn	Move down one screen in the current pane

Esc	Cancel incremental search

Ctrl+N	Find next match for the incremental search

Tab	Move from the Inspector pane to the History list button to the Details pane

The following keyboard commands are shortcuts for setting filters and changing views in the Object Browser.

Key	Action

Ctrl+C	Toggles the Constants filter

Ctrl+F	Toggles the Functions and Procedures filter

Ctrl+T	Toggles the Types filter

Ctrl+V	Toggles the Variables filter

Ctrl+P	Toggles the Properties filter

Ctrl+D	Toggles the Inherited filter

Ctrl+A	Toggles the Virtual filter

Ctrl+1	Toggles the Private filter

Ctrl+2	Toggles the Protected filter

Ctrl+3	Toggles the Public filter

Ctrl+4	Toggles the Published filter

Ctrl+B	Steps back

Ctrl+L	Opens the Object Browser History list

Ctrl+O	Switches to the Objects view

Ctrl+U	Switches to the Units view

Ctrl+G	Switches to the Global view

Ctrl+Y	Switches to the Symbols view

Ctrl+S	Switches to the Scope page of the Details pane

Ctrl+I	Switches to the Inheritance page of the Details page

Ctrl+R	Switches to the Reference page of the Details page


Setting filters

See also

You can use the ObjectBrowser to filter for certain symbols. For example, you can choose to view just the variables in the currently selected object.

The following is a list of filters you can set:

Constants

Functions or procedures

Types

Variables

Properties

Inherited

Virtual

Private

Protected

Public

Published

To set a filter, do one of the following:

Click the filter's associated button.

Select the symbol filter from the Browser page of the Environment Options dialog box. Open the Enviroment Options box from the Tools menu.

Press the filter's keyboard shortcut.

Note:	The glyph next to the symbol in either the Inspector pane or Details pane identifies the symbol type. In some cases more than one glyph can appear next to a symbol. The second letter further describes the symbol, and it appears to the right of the letter identifying the type of symbol.


See also

The ObjectBrowser window

Browsing symbols

ObjectBrowser keyboard shortcuts

Searching for symbols in the ObjectBrowser


ObjectBrowser SpeedMenu

See also

The ObjectBrowser SpeedMenu lets you quickly browse different symbols or change the display of the symbols you are browsing.

To access the ObjectBrowser SpeedMenu:

Right-click anywhere in the ObjectBrowser window.

Press Alt+F10.

The items on the ObjectBrowser SpeedMenu are:

Objects	Displays an inheritance tree for all the objects in your application

Units	Displays the units in the current project

Globals	Displays the global symbols in the entire project

Symbol	Opens the Browse Symbols dialog box

Qualified Symbols	Displays the qualified identifier for a symbol

Sort Always	Sorts the symbols alphabetically by name

Show Hints	Toggles the display of Help Hints on the filter buttons

Info Line	Toggles display of the Info Line


See also

The ObjectBrowser window


Objects (ObjectBrowser SpeedMenu)

Choose Objects from the ObjectBrowser SpeedMenu to display, in the Inspector pane, all the objects in your application, arranged as a vertical inheritance tree to show parent-child relationships.


Units (ObjectBrowser SpeedMenu)

Choose Units from the ObjectBrowser SpeedMenu to list, in the Inspector pane, all the units used by your application.


Globals (ObjectBrowser SpeedMenu)

Choose Globals from the ObjectBrowser SpeedMenu to display, in the Inspector pane, all the global information declared in the units used in your application.


Symbol (ObjectBrowser SpeedMenu)

Choose Symbol from the ObjectBrowser SpeedMenu to opens the Browse Symbol dialog box, where you can specify a symbol to browse.

Browse Symbol dialog box

Use this dialog box to browse a specific symbol.

To browse a specific symbol, do one of the following:

Enter the symbol name in the edit box and click OK.

Click the down arrow to choose from a list of previously entered symbols and click OK.
You can also use the arrow keys to move through the list box.

When you click OK, the ObjectBrowser places the symbol in the Inspector pane and displays its local information in the Details pane.

You can then go to the source code that defines the symbol or inspect the symbol declaration. For more information on browsing symbols in your code, see Browsing symbols.


Qualified Symbols (ObjectBrowser SpeedMenu)

Choose Qualified Symbols from the ObjectBrowser SpeedMenu to display the qualified identifier for a symbol. When this option is off, only the symbol name is displayed.

You can set this option using the Browser page of the Environment Options dialog box. Open the Environment Options box from the Tools menu.


Sort Always (ObjectBrowser SpeedMenu)

Choose Sort Always from the ObjectBrowser SpeedMenu to display the symbols in alphabetical order by symbol name. When this option is off, the symbols sort by declaration order.

You can set this option using the Browser page of the Environment Options dialog box.


Info Line (ObjectBrowser SpeedMenu)

Choose Info Line from the ObjectBrowser SpeedMenu to toggle the display of the Info Line.


About the Component palette

Section topics	See also

Components are the building blocks of every Delphi application, and the basis of the Delphi visual component library. Each page tab in the Component palette displays a group of icons representing the components you can use to design your application interface.  To add a component to an open form, double-click it, then set its properties and code its event handlers.

The Component palette’s Help Hints feature displays a small pop-up window containing the name or brief description of the button when your cursor is over the button for longer than one second. To enable Help Hints, choose Show Hints from the Component palette context menu.

Components may be either visual or non-visual. Each component has specific attributes that enable you to control your application: Properties, Events, and Methods.

To horizontally resize the Component palette, drag the separator line between the Component palette and the SpeedBar.

The following topics list and briefly describe the components on each tab of the Component palette.  Click the component name to see its formal object definition in the Delphi Visual Class Library Reference.  Click links in each component’s description to see relevant topics in the Delphi User’s Guide or Delphi Developer’s Guide.

Standard components

Additional components

Win32 components

System components

Internet components

Data Access components

Data Controls components

Decision Cube components

QReport components

Dialogs components

Win 3.1 components

Samples components

ActiveX components


See also

Overview of components

Two views of components

Choosing the right component

Understanding components in forms

Setting component properties

Customizing the Component Palette

Component palette context menu

Working with event handlers

Understanding components

Responding to user events

Setting component properties

Common component tasks

Example: Creating a Select File dialog box


Standard page components

Section topics	See also

The components on the Standard page of the Component palette make the standard Windows control elements available to your Delphi applications:

MainMenu	Creates menu bar menus for your form. To access events for items in a main menu, add the MainMenu component to a form and double-click it to open the Menu Designer.

PopupMenu	Creates popup menus that appear when the user right-clicks. To access events for items in a popup menu, add the PopupMenu component to a form and double-click it to open the Menu Designer.

Label	Displays text that the user cannot select or manipulate, such as title text or control labels. See About Label.

Edit	Displays an editing area where the user can enter or modify a single line of text.  Edit is one of several Text controls.

Memo	Displays an editing area where the user can enter or modify multiple lines of data. See About Memo.

Button	Creates a pushbutton control that users choose to initiate actions.  See About Button.

CheckBox	Presents an option that a user can toggle between Yes/No or True/False. Use check boxes to display a group of choices that are not mutually exclusive. Users can select more than one check box in a group. See About CheckBox.

RadioButton	Presents an option that a user can toggle between Yes/No or True/False. Use radio buttons to display a group of choices that are mutually exclusive. Users can select only one radio button in a group. See About RadioButton.

ListBox	Displays a scrolling list of choices. See About ListBox and Properties common to list controls.

ComboBox	Displays a list of choices in a combined list box and edit box. Users can enter data in the edit box area or select an item in the list box area.  See About ComboBox.

ScrollBar	Provides a way to change the viewing area of a list or form. You can also use a scroll bar to move through a range of values by increments. See About Scrollbar.

GroupBox	Provides a container to group-related options on a form. See About GroupBox.

RadioGroup	Creates a group box that contains radio buttons on a form. See About RadioGroup.

Panel	Creates panels that can contain other components on a form. You can use panels to create toolbars and status-lines. See About Panel.


Additional page components

Section topics	See also

The components on the Additional page of the Component palette make specialized Windows control elements available to your Delphi applications:

BitBtn	Creates a button component that can display a bitmap. See About BitBtn.

SpeedButton	Provides a button that can display a glyph but not a caption. Speed buttons are often grouped within a panel to create a tool bar.  See About SpeedButton.

MaskEdit	Allows user to enter and edit data, similar to an edit component, but provides a means to specify particular formats, such as a postal code or phone number. MaskEdit shares the properties of other Text controls.

StringGrid	Creates a grid that you can use to display string data in columns and rows. StringGrid.is a superset of the features provided by the DrawGrid component.

DrawGrid	Creates a grid that you can use to display data in columns and rows.  See About DrawGrid.

Image	Displays a bitmap, icon, or metafile.  See About Image.

Shape	Draws geometric shapes including an ellipse or circle, a rectangle or square, or a rounded rectangle or rounded square.  See About Shape.

Bevel	Creates lines or boxes with a three-dimensional, chiseled appearance.  See About Bevel.

ScrollBox	Creates a resizeable container that automatically displays scrollbars when necessary.  See About ScrollBox.

CheckListBox	Displays a scrollable list similar to ListBox except that each item has a check box next to it. See List boxes and Properties common to list controls.

Splitter	Add a splitter to a form between two aligned controls to allow users to resize the controls at runtime by clicking and dragging the split line. See About Splitter.

StaticText	A non-editable text component like Label, except that it includes a window handle, which is useful when the component’s accelerator key must belong to a windowed control.  Use StaticText to provide users with feedback on the current state of the application.

Chart	The chart equivalent of TTable.  Place the component on a form and  right-click it to display the third-party developer’s Help topics.


Win32 page components

Section topics	See also

The components on the Win32 page of the Component palette provide access to 32-bit Windows (Windows95/NT) user interface common controls available to your Delphi applications.

TabControl	Analogous to a divider in a file cabinet or notebook, this component provides a set of mutually exclusive notebook style tabs.

PageControl	A page set used to make a multipage dialog box. Use this control to define multiple logical pages or sections of information within the same window.  See About PageControl.

ImageList	An image list is a collection of same-sized images, each of which can be referred to by its index. Image lists are used to efficiently manage large sets of icons or bitmaps. All images in an image list are contained in a single, wide bitmap in screen device format. An image list may also include a monochrome bitmap that contains masks used to draw images transparently (icon style). To create an image list, add the ImageList component to the form and double-click it to display the Image List Editor.  Also see Properties common to list controls

RichEdit	Rich Text Format memo control. By default, the rich text editor supports font properties, such as typeface, size, color, bold, and italic format.  It also supports format properties, such as alignment, tabs, indents, numbering, and automatic drag and drop of selected text. See About RichEdit and Text controls.

TrackBar	A bar that defines the extent or range of the adjustment, and an indicator that both shows the current value for the control and provides the means for changing the value.  You can set the trackbar orientation as vertical or horizontal, define the length and height of the slide indicator and the slide bar component, define the increments of the trackbar, and whether to display tick marks for the control. See About TrackBar.

ProgressBar	A rectangular bar that “fills” from left to right, like that shown when you copy files in the Windows Explorer. Use this control to provide visual feedback to the user about the progress of long operations or background processes. See About ProgressBar.

UpDown	Up and down arrow buttons to increment and decrement values. See About UpDown.

HotKey	Attaches a hot key to any component. See About HotKey.

Animate	A Windows animation control window that silently displays an Audio Video Interleaved (AVI) clip, a series of bitmap frames like a movie.  See About Animate.

DateTimePicker	Displays a list box for entering dates or times.  Users can select a date from the calendar or select dates or times by scrolling with Up and Down arrows and by typing.  You must have the latest version of COMCTL32.DLL, usually located in the Windows\System or Windows\System32 directory.

TreeView	Lets you control and display a set of objects as an indented outline based on their logical hierarchical relationship. The control includes buttons that allow the outline to be expanded and collapsed. Use a tree view component to display the relationship between a set of containers or other hierarchical elements. See About TreeView.

ListView	Provides a way to display a list in columns. List views display data in a variety of views. See Properties common to list controls

HeaderControl	Use to display a heading above columns of text or numbers. You can divide the control into two or more parts to provide headings for multiple columns.  You can align the title elements left, right, or centered.  You can configure each part to behave like a command button to support a specific function when the user clicks it. See About HeaderControl.

StatusBar	Area to post the status of actions at the bottom of the screen. See About StatusBar.

ToolBar	 Manages tool buttons and other controls, arranging them in rows and automatically adjusting their sizes and positions.  See About ToolBar and Working with toolbars and cool bars.

CoolBar	Displays a collection of windowed controls (CoolBand objects) within movable, resizable bands. The user positions the controls by dragging the sizing grip to the left of each band.  See About CoolBar and Working with toolbars and cool bars.


System page components

Section topics	See also

The components on the System page of the Component palette make specialized system control elements available to your Delphi applications.

Timer	Timer is a nonvisual component that triggers a one-time or repeated event after a measured interval. You write the code that you want to occur at the specified time inside the timer component's OnTimer event. See About Timer

 	PaintBox	Specifies a rectangular area on a form that provides boundaries for application painting.  See About PaintBox

MediaPlayer	Displays a VCR-style control panel for playing and recording multimedia video and sound files. See About MediaPlayer

OleContainer	Creates an Object Linking and Embedding (OLE) client area in a form. See About OleContainer.

DdeClientConv	Establishes a client connection to legacy Dynamic Data Exchange (DDE) server application. See About DdeClientConv

DdeClientItem	Specifies the Dynamic Data Exchange (DDE) client data to transfer during a DDE conversation. See About DdeClientItem

DdeServerConv	Establishes a server connection to legacy Dynamic Data Exchange (DDE) client application. See About DdeServerConv

DdeServerItem	Specifies the Dynamic Data Exchange (DDE) server data to transfer during a DDE conversation. See About DdeServerItem


Internet page components

Section topics	See also

The components on the Internet page (available only in Delphi Client/Server edition) of the Component palette offer a variety of internet access protocols for your Delphi applications:

ClientSocket	Add to a form or data module to turn an application into a TCP/IP client. ClientSocket specifies a desired connection to a TCP/IP server, manages the open connection, and terminates the completed connection. See Using client sockets and Working with sockets

ServerSocket	Add to a form or data module to turn an application into a TCP/IP server.  ServerSocket listens for requests for TCP/IP connections from other machines and establishes connections when requests are received.  See Using server sockets and Working with sockets

WebDispatcher	Converts an ordinary data module to a Web module and enables the Web server application to respond to HTTP request messages.  See About WebDispatcher and The Structure of a Web server application

PageProducer	Converts an HTML template into a string of HTML commands that can be interpreted by a client application such as a Web browser. The commands and HTML-transparent tags are replaced with customized content by the OnHTMLTag event.  See Using page producer components

QueryTableProducer	Assembles a sequence of HTML commands to generate a tabular display of the records from a TQuery object, which obtains its parameters from an HTTP request message.  See Using TQueryTableProducer

DataSetTableProducer	Assembles a sequence of HTML commands to generate a tabular display of the records from a TDataSet object.  This allows an application to create images of a dataset for an HTTP response message. See Using TDataSetTableProducer

FTP	Invisible ActiveX control provides easy access for Internet File Transfer Protocol (FTP) services for transferring files and data between a remote and local machine.

HTML	Invisible ActiveX control implements an HTML viewer, with or without automatic network retrieval of HTML documents, and provides parsing and layout of HTML data, as well as a scrollable view of the selected HTML page. The HTML component can also be used as a non-visual HTML parser to analyze or process HTML documents.

HTTP	Invisible ActiveX control implements the HTTP Protocol Client, allowing users to directly retrieve HTTP documents if no browsing or image processing is necessary.

NNTP	Invisible ActiveX Client Control allows applications to access Networking News Transfer Protocol (NNTP) news servers. It provides news reading and posting capabilities.

POP	Invisible ActiveX control that retrieves mail from UNIX or other servers supporting POP3 protocol.

SMTP	ActiveX control that gives applications access to SMTP mail servers and mail posting capabilities.

TCP	Invisible Winsock ActiveX Control implements the Transmission Control Protocol for both client and server applications, providing easy access to TCP network services.

UDP	Invisible WinSock ActiveX Control provides easy access to User Datagram Protocol (UDP) network services. It implements WinSock for both client and server and represents a communication point utilizing UDP network services. It can also be used to send and retrieve UDP data.


Data Access page components

Section topics	See also

The components on the Data Access page of the Component palette make specialized database access functionality available to your Delphi applications:

DataSource	Acts as a conduit between a TTable, TQuery, TStoredProc component and data-aware components, such as TDBGrid.  See Working with data sources

Table	Retrieves data from a physical database table via the BDE and supplies it to one or more data-aware components through a DataSource component. And conversely, sends data received from a component to a physical database via the BDE. See Working with tables

Query	Uses SQL statements to retrieve data from a physical database table via the BDE and supplies it to one or more data-aware components through a TDataSource component.  Conversely, uses SQL statements to send data from a component to a physical database via the BDE. See Working with queries

StoredProc	Enables an application to access server stored procedures. Sends data received from a component to a physical database via the BDE. See Working with stored procedures

Database	Sets up a persistent connection to a database, especially a remote database requiring a user login and password. See Connecting to data bases

Session	Provides global control over a group of TDatabase components that you associate with it. A default TSession component is automatically created for each Delphi database application. You need to use the TSession component only if you are creating a multithreaded database application. Each database thread requires its own session component. See Managing database sessions

BatchMove	Copies a table structure or its data. Can be used to move entire tables from one database format to another.  See Copying data and table structures

UpdateSQL	Lets you use Delphi's cached updates support with read-only datasets. For example, you could use a TUpdateSQL component with a "canned" query to provide a way to update the underlying datasets, essentially giving you the ability to post updates to a read-only dataset. You associate a TUpdateSQL component with a dataset by setting the dataset's UpdateObject property. The dataset automatically uses the TUpdateSQL component when cached updates are applied.  See About UpdateSQL

Provider	Encapsulates the mechanism for providing data from a multi-tiered application server to a client dataset in a desktop client application.  See Working with a provider component and Creating a multi-tiered application

ClientDataSet	Implements a database-independent dataset component that can be used independently in a single-tiered application, or as a client dataset in the client application portion of a multi-tiered database application.  See Creating and using a client dataset

RemoteServer	Establishes a DCOM connection to a remote server in a client application that is part of a multi-tiered application.  See Establishing and maintaining a connection to an application server

MIDASConnection	Establishes a DCOM, socket, or OLEnterprise connection to a remote server in a client application that is part of a multi-tiered application.  See Establishing and maintaining a connection to an application server


Data Controls page components

Section topics	See also

The components on the Data Controls page of the Component palette make specialized database control elements available to your Delphi applications.:

DBGrid	Data-aware custom grid that enables viewing and editing data in a tabular form similar to a spreadsheet. Makes extensive use of TField properties (set in the Fields editor) to determine a column's visibility, display format, ordering, and so on.  See Working with grids

DBNavigator	Data-aware navigation buttons that move a table's current record pointer forward or backward. The navigator can also place a table in Insert, Edit, or Browse state, post new or modified records, and retrieve updated data to refresh the display. See Navigating and manipulating records

DBText	Data-aware label that displays a field value in the current record. See Displaying data as labels

DBEdit	Data-aware edit box that displays or edits a field in the current record. See About DBEdit See About DBEdit

DBMemo	Data-aware memo box that displays or edits BLOB text in the current record. See About DBMemo

DBImage	Data-aware image box that displays, cuts, or pastes bitmapped BLOB images to and from the current record. See About DBImage

DBListBox	Data-aware list box that displays a scrolling list of values from a column in a table. See About DBListBox

DBComboBox	Data-aware combo box that displays or edits a scrolling list of values from a column in a table. See About DBComboBox

DBCheckBox	Data-aware check box that displays or edits a Boolean data field from the current record. See About DBCheckBox

DBRadioGroup	Data-aware group of radio buttons that display or set column values. See About DBRadioGroup

DBLookupListBox	DBLookupListBox is a data-aware list box that derives its list of display items from either a Lookup field defined for a dataset or a secondary data source, data field, and key. In either case, a user is presented with a restricted list of choices from which to set a valid field value. When a user selects a list item, the corresponding field value is changed in the underlying dataset. See About DBLookupListBox.
To specify list box items using a lookup field, the dataset to which you link the control must already define a lookup field.  See Defining a lookup field

DBLookupComboBox	DBLookupComboBox is a data-aware combo box that derives its drop-down list of display items from either a lookup field defined for a dataset or a secondary data source, data field, and key. In either case, a user is presented with a restricted list of choices from which to set a valid field value. When a user selects a list item, the corresponding field value is changed in the underlying dataset. See About DBLookupComboBox.
To specify combo box list items using a lookup field, the dataset to which you link the control must already define a lookup field. See Defining a lookup field

DBRichEdit	A multiline edit control that can display and edit a rich text memo field in a dataset. See About DBRichEdit

DBCtrlGrid	A DBCtrlGrid control displays multiple fields in multiple records in a tabular grid format. Each cell in the grid displays multiple fields from a single record. See About DBControlGrid.

DBChart	Place the component on a form and right-click it to display the third-party developer’s Help topics.


Decision Cube page components

Section topics	See also	About Decision Cube

The components on the Decision Cube page of the Component palette (available only in Delphi Client/Server edition) add multidimensional data analysis features to your Delphi applications. The Decision Cube components provide cross tabulation of tables and graphs, drill down, pivot, and aggregation to help users visual data for decision-making purposes:

DecisionCube	A multidimensional data store. See Using decision cubes

DecisionQuery	Specialized form of TQuery used to define the data in a decision cube. See Using decision queries

DecisionSource	Defines the current pivot state of a decision grid or a decision graph.  See Using decision sources

DecisionPivot	Use to open or close decision cube dimensions or fields by pressing buttons.  See Using decision pivots

DecisionGrid	Displays single and multidimensional data in table form.  See Creating and Using decision grids.

DecisionGraph	Displays fields from a decision grid as a dynamic graph that changes when dimensions are modified. See Using decision graphs


See also

Guidelines for using decision support components

Using decision support components


QReport page components

Section topics	See also

The Quick Report components on the QReport page of the Component palette enables you to visually design Quick Reports.  You build reports with bands, adding titles, page headers and footers, multiple detail sets, summaries, group headers and footers.  You can report from any DataSource, including TTable, TQuery, lists, arrays, and so on.  Use the on-screen preview to check your results.  Automatically perform calculations like summary and counting of fields.  You can reset calculations at group level.

To see the third-party developer’s help, place the component on a form and right-click it.

QuickRep	The basic report form on which you build all your reports. It is a visual component which takes the shape of the currently selected paper size. You create reports by dropping bands and printable components on the TQuickRep component and connecting it to a dataset.

QRSubDetail	Links additional datasets into a report. Typically you would set up a master detail relationship between table or query components and create a similar relationship with TQRSubDetail components.

QRBand	Drop bands on a TQuickRep component and set the BandType property to tell how the band will behave during report generation.

QRChildBand	If you have bands with expanding components and want other components to be moved down accordingly you can create a child band and put the moving components on it. It’s also useful if you have very long bands that span multiple pages.

QRGroup

QRLabel	Prints static or other non-database text. Enter the text to be displayed in the Caption property. You can split text on multiple lines and even multiple pages.

QRDBText	A data-aware version of the TQRLabel that prints the value of a database field. Calculated fields and text field types can be printed, including String fields, various numeric fields, date fields and memo fields. Text can span multiple lines and pages. You connect the component to the data field by setting the DataSource and DataField properties. Unlike regular data-aware components, TQRDBText works even with dataset controls disabled to improve speed.

QRExpr	Prints database fields, calculations, and static text. Input a valid QuickReport expression in the Expression property.

QRSysData	Prints system information such as report title, current page number, and so on. Select the data to print in the Data property. Set any preceding text in the Text property.

QRMemo	Prints a large amount of text that does not come from a database field. It can be static text or you can change it during report generation. You can set the field to expand vertically as needed and then span multiple pages if necessary.

QRRichText

QRDBRichText

QRShape	Draws simple shapes like rectangles, circles, and lines on a report.

QRImage	Displays a picture on a report. Supports all image formats supported by the Delphi TPicture class.

QRDBImage	Prints images stored in binary (BLOB) fields. Prints all graphics formats supported by Delphi.

QRCompositeReport

QRPreview

QRChart


Dialogs page components

Section topics	See also

The components on the Dialogs page of the Component palette make the Windows common dialog boxes available to your Delphi applications. The common dialog boxes provide a consistent interface for file operations such as opening, saving, and printing files:

A common dialog box opens when its Execute method is called. Execute returns one of the following Boolean values:

True, if the user chooses OK to accept the dialog box

False, if the user chooses Cancel or escapes from the dialog box without saving any changes.

Each common dialog box component (except the PrinterSetup component) has a set of properties grouped under Options in the Object Inspector. The Options properties affect the appearance and behavior of the common dialog boxes. To display the Options properties, double-click Options in the Object Inspector.

To close a dialog box programmatically, use the CloseDialog method.

To manipulate the position of a dialog box at run time, use the Handle, Left, Top, and Position properties.

OpenDialog	Displays a Windows common Open dialog box. Users can specify the name of a file to open in this dialog box. See Selecting files to open

SaveDialog	Displays a Windows common Save dialog box. Users can specify the name of a file to save in this dialog box. See Saving files

OpenPictureDialog	Displays a modal Windows dialog box for selecting and opening graphics files. Identical to Open dialog box except that it has an image preview region. See Selecting files to open

SavePictureDialog	Displays a modal Windows dialog box for entering file names and saving graphics files. Identical to Save dialog box except that it has an image preview region. See Saving files

FontDialog	Displays a Windows common Font dialog box that lets users can specify font, size, and style information.  About FontDialog

ColorDialog	Displays a Windows common Color dialog box that lets users specify color information.

PrintDialog	Displays a Windows common Print dialog box that lets users specify printing information, such as a range of pages and the number of copies.  About PrintDialog

PrinterSetupDialog	Displays a Windows common Printer Setup dialog box that lets users change and set up printers.  About PrinterSetupDialog

FindDialog	Displays a Windows common Find dialog box that lets users specify a string to search for.  About Windows Common Dialogs

ReplaceDialog	Displays a Windows common Replace dialog box that lets users specify a search string and a replacement string. About Windows Common Dialogs


Windows 3.1 Components
Windows 3.1 page components

Section topics	See also

The components on the Windows 3.1 page of the Component palette provide Windows 3.1 control elements for backward compatibility with Windows 3.1 applications built with previous versions of Delphi. Many of these older controls implement the same behavior as more recent specialized 32-bit Windows controls.

Important:  When creating new applications, do not use these controls.
The following table indicates which control should be used instead:

Win 3.1 control	Replace with	Palette page of new control

DBLookupList	DBLookupListBox	Data Controls

DBLookupCombo	DBLookupComboBox	Data Controls

TabSet	TabControl	Win32

Outline	TreeView	Win32

Header	HeaderControl	Win32

NoteBook	PageControl	Win32

TabbedNoteBook	PageControl	Win32

DBLookupList	Data-aware list box that displays values looked up from columns in another table at run time.

DBLookupCombo	Data-aware combo box that displays values looked up from columns in another table at run time.

TabSet	Creates notebook-like tabs. You can use the TabSet component with the Notebook component to enable users to change pages.

Outline	Displays information in a variety of outline formats.

TabbedNotebook	Creates a component that contains multiple pages, each with its own set of controls. Users select a page by clicking the tab at the top of the page

Notebook	Creates a component that can contain multiple pages. Used with the Notebook component, it enables users to change pages.

Header	Creates a sectioned region for displaying data. Users can resize each section of the region to display different amounts of data.

FileListBox	Displays a scrolling list of files in the current directory.

DirectoryListBox	Displays the directory structure of the current drive. Users can change directories in a directory list box.

DriveComboBox	Displays a scrolling list of available drives.

FilterComboBox	Specifies a filter or mask to display a restricted set of files.


Samples page components

Section topics	See also

The components on the Sample page of the Component palette are examples of customized components that you can build and add to the Component Palette. Source code to these sample components is included in the DELPHI\SOURCE\SAMPLES directory of the default installation.

Gauge

ColorGrid

SpinButton

SpinEdit

DirectoryOutline

Calendar

IBEVentAlerter


ActiveX page components

Section topics	See also

The components on the ActiveX page of the Component palette are ActiveX objects, complete, portable working applications created by third-party developers.  To use these components, you must first open an ActiveX form with a current ActiveX library project.  After placing a component on the ActiveX form, right-click it to display Properties and other commands or dialog boxes for configuring the component’s functionality or setting its values.  The Properties dialog box or other controls contain Help buttons that display the developer’s Help system for the component.

Chartfx	Lets you create highly customized charts.  Choose Properties to display a tabbed control panel that lets you define the values, appearance, and end-user functionality of the chart component.

VSSpell	Visual Speller, a customizable spelling checker.

F1Book	Formula One, a spreadsheet including a full featured Designer.

VtChart	Lets you create true 3D charts.

Graph	Pinnacle Graph, another implementation of a 2D graph designer.


Common component tasks

See also

When designing your application interface, there are procedures you might want to perform that are not specific to a particular component. The list below represents a sampling of such procedures. Choose a topic for more information.

Tasks

Providing Help Hints

Aligning components

Handling keyboard events

Mouse events

Handling drag and drop events

Setting the focus

Setting the tab order

Enabling and disabling components


See also

Component palette

Customizing the Component Palette


Using the Form component

See also

An application usually contains multiple forms: A main form, which is the primary user interface, and other forms such as dialog boxes, secondary windows (for instance, those that display OLE 2.0 data), and so on. You can begin your form design from one of the many Form templates provided with Delphi. You can save any form you design as a template that you can reuse in other projects.

Forms, the basic building blocks

Adding a field to a form object

Developing custom dialog boxes

Reusing forms as DLLs

Making your form a component

Tasks

To make the form stay on top of other open windows (for instance, the Project Manager or Alignment Palette) at run time, set the FormStyle property to fsStayOnTop.

To remove the form's default scrollbars, change the value of the HorzScrollBar and VertScrollBar properties.

To make the form an MDI frame or MDI child, use the FormStyle property.

To change the form's border style, use the BorderIcons and BorderStyle properties. (The results are visible at run time.)

To change the icon for the minimized form, use the Icon property.

To specify the initial position of a form in the application window, use the Position property.

To specify the initial state of the form, (e.g., minimized, maximized or normal) use the WindowState property.

To define the working area of the form at run time, use the ClientHeight and ClientWidth properties. (Note that ClientHeight and ClientWidth represent the area within the form's border; Height and Width represent the entire area of the form.)

To specify which control has initial focus in the form at run-time, use the ActiveControl property.

To pass all keyboard events to form, regardless of the selected control, use the KeyPreview property.

To specify a particular menu, if your form contains more than one menu, use the Menu property.


See also

Component palette

Customizing the Component Palette

Form basics

TForm reference


Responding to user events
About user events

See also

In event-driven programming, user events are a key part of your application logic. User events correspond to the elements in your Graphical User Interface (GUI). For example, most components contain an OnClickevent that can be programmed to respond when the user clicks the component. Other events, such as the form's OnActivate are not triggered by the user, but by your program code. The code you write to respond to events is called an Event Handler.

For information about handling specific user events, choose from the following topics.

Button click events

Keyboard events

Mouse events

Drag and drop events


See also

Creating a user interface

Working with event handlers


Responding to keyboard events
About keyboard events

See also

Delphi provides three events that enable you to capture user keystrokes:

OnKeyDown

OnKeyPress

OnKeyUp

You can write event handlers for these events to respond to any key or key combination the user might press at run time.

Note:	Responding when the user presses short-cut or accelerator keys, such as those provided with menu commands, does not require writing event handlers.

There are several important considerations when handling keyboard events. Choose a topic for more information.

Keyboard event processing order

Processing keystrokes

Redirecting keyboard events to the form


See also

Responding to user events

Adding, inserting, and deleting menu items and keyboard shortcuts.

TShiftState


Keyboard event processing order

See also	Example

Keyboard events are received at several levels:

The application level, with an Application.OnMessage event.

You will rarely need to intercept keystrokes at the application level, but it is important to know that this first level is available.

The "shortcut-key" level

When you specify a short-cut key, such as those provided as a property of menu items, the keystroke is intercepted before the form sees it.

The form level

The form contains a KeyPreview property that enables you to code "global" keystroke events.

The component level

When you program key-press event handlers at the component level, the component with focus intercepts the keystroke.


See also

Responding to keyboard events

Processing keystrokes

Redirecting keyboard events to the form


Example

The following code uses two buttons in a form to demonstrate the order in which keyboard events are processed by your application.

When you first run the program and press Alt+Ctrl, the form turns purple, because Button1 had focus and therefore receives the keystrokes. If you click Button1 to disable it, or click Button2 to set the form's KeyPreview on, and then press Ctrl+Alt again, the form turns aqua because the form receives the keystrokes.

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);

begin

 if (shift = ([ssAlt, ssCtrl])) then form1.color := clAqua;

end;

procedure TForm1.Button1KeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);

begin

   if (shift = ([ssAlt, ssCtrl])) then form1.color := clPurple;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

 button1.enabled := false;

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

 Form1.Keypreview := True;

end;


Processing keystrokes

See also	Example

Every keystroke generates an OnKeyDown and OnKeyUp event. In addition, keys that have an ASCII equivalent generate an OnKeyPress event. Naturally, your code does not need to capture each keystroke, but it is important to know the order in which keystroke events are processed. See the attached example for an explicit demonstration.

Your application can capture each event at several levels (see KeyBoard event processing order). OnKeyPress returns a single ASCII character, while OnKeyDown and OnKeyUp contain parameters that reflect information about whether control keys such as Alt, Ctrl and Shift keys were pressed at the time the last keystroke occurred.

For instance, here is the OnKeyDown, OnKeyPress and OnKeyUp keystroke sequence generated when the user presses Shift+D:

KeyDown (Shift)

KeyDown (Shift+D)

KeyPress (D)

KeyUp (Shift+D)

KeyUp

When keys are pressed in combination, the OnKeyDown event passes the key for each previous OnKeyDown to the next OnKeyDown. The OnKeyPress event, by contrast, merely returns the last key pressed. However, OnKeyPress returns a different ASCII character for 'd' and 'D,' but OnKeyDown and OnKeyUp do not make a distinction between uppercase and lowercase alpha keys.


See also

Responding to keyboard events

Keyboard event processing order

Redirecting keyboard events to the form


Example

The following code uses a list box to display the keystroke processing order of the OnKeyUp, KeyDown, and OnKeyPress events of the form and the Edit component for any key you press.

Note:	By adding a Default and Cancel button to the form (buttons whose Default and Cancel properties are set to True, respectively) you can view how the Esc and Enter keystrokes are processed when such buttons exist.

procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);

begin

 Listbox1.Items.Add('Edit1.KeyDown'+ShortCutToText(ShortCut(Key, Shift)));

end;

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);

begin

   Listbox1.Items.Add('Edit1.KeyPress'+ Key);

end;

procedure TForm1.Edit1KeyUp(Sender: TObject; var Key: Word;

  Shift: TShiftState);

begin

 Listbox1.Items.Add('Edit1.KeyUp'+ShortCutToText(ShortCut(Key, Shift)));

end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);

begin

  Listbox1.Items.Add('Form1.KeyDown'+ShortCutToText(ShortCut(Key, Shift)));

end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);

begin

   Listbox1.Items.Add('Form1.KeyPress'+ Key);

end;

procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word;

  Shift: TShiftState);

begin

  Listbox1.Items.Add('Form1.KeyUp'+ShortCutToText(ShortCut(Key, Shift)));

end;


Responding to the OnKeyPress event

See also	Example

REDUNDANT WITH CURRENT VCL

The OnKeyPress event is the simplest of the three events, in that it returns only a single character the user presses. The character must fall within the ASCII character set.

OnKeyPress interprets key combinations only insofar as they evaluate to a single ASCII-character. So, for example, OnKeyPress recognizes the result of Shift+A (a capital 'A'), but not the individual keys pressed. OnKeyPress does not otherwise evaluate keys combinations, and does not recognize function keys or non-ASCII keys such as Ctrl, Alt, Insert, Page Down, and so on.

Default or Cancel buttons in the form will intercept the Enter and Escape key press for both the OnKeyPress and OnKeyDown events, (unless used in combination with Alt for the OnKeyDown.)


See also

Responding to keyboard events

Keyboard event processing order

Processing keystrokes

Redirecting keyboard events to the form


Example

The following example demonstrates how OnKeyPress and OnKeyDown can be coded to handle keyboard events.

The OnKeyPress event contains a key parameter of type Char. Therefore if you want to test for which key the user pressed, you simply enter the character.

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);

begin

 case key of

   'I': Panel1.Caption := 'Shift+I was pressed';

   'c': Panel1.caption := 'c was pressed';

   ' ': Panel1.caption := 'the space bar was pressed';

 end;

end;

With OnKeyDown, the key parameter is of type Word. Therefore if you want to test for which key the user pressed, you must refer to the equivalent Virtual Key Codes (see the Win32 Programmer's Reference ).

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);

begin

  case key of

   vk_Insert: Panel1.Caption := 'INS';

   vk_Capital: Panel1.caption := 'CAP';

   vk_Numlock: Panel1.caption := 'NumLock';

  end;

end;


Responding to the OnKeyUp and OnKeyDown events

See also	Example

Use the OnKeyDown and OnKeyUp events when you want to interpret key combinations such as whether the SHIFT, CTRL, or ALT key is pressed at the time the active control receives the key event; and to handle keys that have no ASCII equivalent, such as function keys. The F1 key, for example, does not get captured by the OnKeyPressevent because it has no alphanumeric value.  See Responding To The OnKeyPress Event

While both key events return the value of the keys pressed, the OnKeyDown event is much more commonly used. You might use OnKeyUp when you want to initiate a background process inbetween the key-down and key-up. When the user presses and holds down a key, the key returns repeated OnKeyDown events until the user releases it, at which time a single OnKeyUp is returned. In programs such as games, these specific keyboard interactions become more useful. 'Vk_Insert' is the virtual key code for the Insert key.

Note:	To use OnKeyDown or OnKeyUp to test for keys the user presses, you must use Virtual key codes (see the Win32 Programmer's Reference ) to specify the key. This is because the parameter key is of type word. For example, the following event handler specifies that when the user presses the Insert key, the panel in the form displays 'INS.' '

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);

begin

  if key = vk_Insert then Panel1.Caption := 'INS';

end;


See also

Responding to keyboard events

Keyboard event processing order

Processing keystrokes

Redirecting keyboard events to the form


Example

The following two event handlers respond to the OnKeyDown and OnKeyUp events by zooming and shrinking a graphical image with the user presses and releases the 'Z' key.

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);

begin

  if chr(Key) = 'Z' then Image1.Stretch := True;

end;

procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word;

  Shift: TShiftState);

begin

  if chr(Key) = 'Z' then image1.Stretch := False;

end;


Redirecting keyboard events to the form

Example

Set the KeyPreview property of a form to True.

You can now handle keyboard events at the form level, rather than having to write separate event handlers for every component in the form that might have focus when the keyboard event occurs. The form can receive any keystrokes that the focused component can receive. Also, by using KeyPreview, you can then code unique keyboard event handlers for specific components.

KeyPreview is like having an automatic call to the form-level keyboard event handler at the start of every component-level keyboard handler. The component still sees the event, but the form has an opportunity to handle it first. For example, you could write an event handler for the FormKeyDown that performs key mappings so that a ButtonKeyDown receives the mapped key instead of the key originally pressed.


See also

Responding to keyboard events

Keyboard event processing order

Processing keystrokes


Example

The following example demonstrates how the form KeyPreview property intercepts keystrokes.

Place a button on a form, and write the following handler for the form OnKeyDown event:

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);

begin

 if (shift = ([ssAlt, ssCtrl])) then Form1.color := clAqua;

end;

Run the application and press Alt+Ctrl.

Nothing happens -- this is because the control with focus receives the keyboard events, and in this case no keyboard event handler was written for the button.

You could solve this by disabling the button, or by putting the code in the button event handler. But by setting KeyPreview for the form to True, you can intercept keystrokes at the form level instead of writing additional code.

Set the form's KeyPreview to True, and run the program again. This time the form receives the keystrokes -- when you press Ctrl+Alt, the form turns aqua.


Creating a Select File dialog box

1. Place the following controls on a form:

DriveComboBox named DriveCombo1

DirectoryListBox named DirList1

Edit component named FileEdit1

FilterComboBox named Filter1

FileListBox name FileList1

Label component named PathLabel

2. Select DriveCombo1 and set the DirList := DirList1.

3. Select DirList1. Set DirLabel := PathLabel. and FileList := FileList1.

4. Select Filter1. Set FileList := FileList1 and set the Filter property as desired.

5. Select FileList1. Set the FileEdit := FileEdit1. If you want to select multiple file names, set  MultiSelect := True.

6. Code the event handlers for each of the following:

Form OnCreate event

Filter1 OnChange event

DriveCombo1 OnChange event

FileList1 OnChange event


Event handler code for Form OnCreate event

begin

  FileList1.Mask := Filter1.Mask;

  Filter1Change(Filter1);

  PathLabel.Caption := DirList1.Directory;

end;


Event handler code for Filter1 OnChange event

begin

  FileEdit.Text := FileList1.Mask;

  PathLabel.Caption := DirList1.Directory;

end;


Event handler code for DriveCombo1 Onchange Event

begin

  PathLabel.Caption := DirList1.Directory;

end;


Event handler code for FileList1 OnChange event

begin

  PathLabel.Caption := DirList1.Directory + '\' + FileList1.Filename;

end;


About the Timer component

TTimer reference	Common component tasks

Purpose

Use the Timer component to trigger an event, either one time or repeatedly, after a measured interval. Write the code that you want to occur at the specified time inside the timer component's OnTimer event.

Tasks

To specify the amount of elapsed time before the timer event is triggered, use the Interval property.

To discontinue a timed event, set the timer component's Enabled property to False.

Displaying a SplashScreen


Displaying a SplashScreen

The following two event handlers display and close a form called SplashScreen before the application's main form opens. The constant Startup is declared in Form1's interface part. The first event handler calls the Show method of SplashScreen from Form1's OnActivate event.

procedure TForm1.FormActivate(Sender: TObject);

begin

  if Startup then

  begin

    Startup := False;

    SplashScreen.Show;

  end;

end;

SplashScreen contains a Timer component whose Interval property is set to 3000, so the form is displayed for three seconds and then closes. The form's Close method is attached to the timer component's OnTimer event.

procedure TForm2.Timer1Timer(Sender: TObject);

begin

  Close;

end;


About the PaintBox component

TPaintBox reference	Common component tasks

Purpose

Provides a means of limiting drawing on a form to the specific rectangular area encompassed by the PaintBox.

Tasks

To access the drawing "surface" of the PaintBox, use the Canvas property.

To draw on the canvas of the PaintBox, write appropriate code in the event handler for the OnPaint event.


About the MediaPlayer component

See also	TMediaPlayer reference

Purpose

Use the MediaPlayer component to enable your application to control a media playing or recording device such as a CD-ROM player, video player/recorder, or MIDI sequencer.

Deciding how to use the MediaPlayer

How you use and configure the media player component depends largely on the type of device you want to control with it, and whether you want to use all the capabilities of the device or limit your application to certain capabilities.

For example, if you are controlling a video cassette recorder and do not want your user to accidentally erase the tape, you could disable or hide the Record segment of the MediaPlayer component.

If the medium has tracks, as does a compact disc, you might not need to use the Rewind capability of the MediaPlayer; whereas you would need this for a video tape or Digital Audio Tape (DAT) device.

Tasks for controlling component functions

To show or hide individual button segments on the MediaPlayer component, use the VisibleButtons property.

To enable or disable individual segments on the Media Player component, use the EnabledButtons property.

To control whether or not the device automatically rewinds at the end of the medium before playing or recording, use the AutoRewind property.

To determine whether or not other components or applications can access the device, use the Shareable property.

Tasks for controlling device access

To specify or change the type of device controlled by the MediaPlayer component, use the DeviceType property.

To specify or change the starting position within the currently loaded medium, use the Start property.

To specify or change the current position of the medium, use the Position property.

Tasks for accessing information on the medium

To specify or change the media file to play or record, use the FileName property

To determine the ID of the current device, use the run-time/read-only property DeviceID.

To determine the position within the currently loaded medium from which playing or recording will start, use the run-time/read-only property StartPos.

To determine the number of tracks on the open multimedia device, use the run-time/read-only property Tracks.

To determine the length of a track, use the run-time/read-only property TrackLength for the current TrackNum index.

To determine the length of a track, use the run-time/read-only property TrackPosition for the current TrackNum index.

Tasks for controlling device operation

The MediaPlayer component provides a number of methods for controlling the operation of a media play or record device. Some of these correspond to one of the button segments of the component (indicated by an asterisk (*)).  You have the option of calling any of these methods in your program code as well.

Here are the methods (listed alphabetically) for controlling a device:

*Back	*Pause	Rewind

Close	PauseOnly	*StartRecording

*Eject	*Play	*Step

*Next	*Previous	*Stop

Open	Resume


See also

Common component tasks

Also see Windows Multimedia Reference


About the OLEContainer component

TOLEContainer reference	Using OLE Automation

Purpose

Use the OLEContainer component to provide your application with the ability to link and embed objects from an OLE server.

When you activate an object inside the OLE container, control transfers to the OLE server application, so the user can access all the functionality of the server application from within your container application.


About the DDEClientConv component

TDDEClientConv reference	About DDE

Purpose

DDE is an older technology for interapplication communication.  For new projects that need not access legacy DDE server applications, you should instead use OLE Automation.

Use the DDEClientConv component to provide your application with the ability to establish a DDE conversation with a legacy DDE server application.  When you place this component on your form, your application becomes a DDE client.

This component works in conjuction with the DDEClientItem component to make your application a complete DDE client.

Tasks

Controlling other applications using DDE

Establishing a link with a DDE client

Establishing a link with a DDE server

Poking data

Creating DDE client applications

To specify the topic of a DDE conversation, use the DDETopic property.

To specify the DDE server application, use the DDEService property.  Depending on the server, the server application name is usually but not always the executable.

To specify the executable name for the DDE server application, use the ServiceApplication property.

To define whether the connection to the DDE server application is established automatically when the form runs, use the ConnectMode property.

To filter characters out of the text data transfer from a server application, use the FormatChars property.


About the DDEClientItem component

TDDEClientItem reference	About DDE

Purpose

DDE is an older technology for interapplication communication.  For new projects that need not access legacy DDE server applications, you should instead use OLE Automation.

Use the DDEClientItem component to define the item of a DDE conversation..  Use it in conjunction with a DDEClientConv component to make your application a DDE client to access legacy DDE server applications.  If you will need only one item, the DDEClientConv is not necessary.

Tasks

Controlling other applications using DDE

Establishing a link with a DDE client

Establishing a link with a DDE server

Poking data

Creating DDE client applications

To specify the DDE client conversation component, use the DDEConv property.

To specify the item of the DDE conversation, use the DDEItem.

To find out what the DDE Server sent, read the Lines property.


About the DDEServerConv component

TDDEServerConv reference	About DDE

Purpose

DDE is an older technology for interapplication communication.  For new projects that need not support legacy DDE client applications, you should instead use OLE Automation.

Use the DDEServerConv component to provide your application with the ability to establish a DDE conversation with DDE client applications. When you place this component on your form, your application becomes a DDE server.

This component works in conjuction with the DDEServerItem component to make your application a complete DDE server.

Tasks

Creating DDE server applications

Establishing a link with a DDE client

Establishing a link with a DDE server


About the DDEServerItem component

TDDEServerItem reference	About DDE

Purpose

DDE is an older technology for interapplication communication.  For new projects that need not support DDE client applications, you should instead use OLE Automation.

Use the DDEServerItem component to define the topic of a DDE conversation with another application.

Use it in conjunction with a DDEServerConv component to make your application a DDE server.

Tasks

Creating DDE server applications

Establishing a link with a DDE client

Establishing a link with a DDE server

To specify the DDE server conversation component, use the ServerConv property.


Database Explorer

The Database Explorer enables you to maintain a persistent connection to a remote database server during application development and to work with BDE aliases and metadata objects.  With the Database Explorer, you can create, view, and modify:

BDE aliases.

Metadata objects such as tables, views, triggers, and stored procedures.

Users and server security information (not implemented in this field test).

The Databases pane of the Database Explorer displays all the valid aliases defined. Select an alias to display the definition of the alias.

To connect to the database specified by an alias,

1.	Select the alias in the Databases.

2.	Do one of the following,

Choose Object|Open.

From the Database Explorer SpeedMenu, choose Open.

When you are connected to a database, the icon in the left pane is surrounded by a green box.

To expand a database,

In the Databases pane, click "+” next to the alias you want to view. The native server object types expand beneath the icon.

Once connected to a database, you can perform SQL operations on the database.

To perform SQL operations,

1.	Select the Enter SQL tab.

2.	Enter SQL statements in the statement area.

3.	Click the Run button.

Your SQL statements will execute and the results will be displayed in the table grid.

For more information, open the Help menu within the Database Explorer and choose a command from it.


Edit Breakpoint dialog box

See also

Use the Edit Breakpoint dialog box to add a breakpoint or to change an existing breakpoint. The breakpoint appears in the Code Editor and the Breakpoints List window.

You can set a breakpoint that interrupts program execution in one of the following situations:

When a line of code you specify is executed.

When a condition you specify occurs as a line of code is executed.

When a pass count occurs. A pass count is a condition or line number that is executed a certain number of times.

Use this dialog box to specify where you want a breakpoint to occur:

To create a breakpoint based on a line number, enter a value for Filename and Line Number.

To create a breakpoint based on a condition, enter a value for Filename, Line Number, and Condition.

To create a breakpoint based on the number of times a line is executed, enter a value for Filename, Line Number, and Pass Count.

To create a breakpoint based on the number of times a condition occurs as a line of code executes, enter a value for Filename, Line Number, Condition, and Pass Count.

To open the Edit Breakpoint dialog box, do one of the following:

Choose Run|Add Breakpoint.

Choose Add Breakpoint from the Breakpoint List SpeedMenu.

Right-click an existing breakpoint in the Breakpoint List window and choose Edit Breakpoint from the Breakpoint List SpeedMenu.

Dialog box options

Filename

Sets or changes the program file for the breakpoint. Enter the name of the program file for the breakpoint.

Line Number

Sets or changes the line number for the breakpoint. Enter or change the line number for the breakpoint.

Condition

Specifies a conditional expression that is evaluated each time the breakpoint is encoutered. Program execution stops when the expression evaluates to True. Enter a conditional expression to stop program execution.

You can enter any valid language expression. However, all symbols in the expression must be accessible from the breakpoint's location, and the expression cannot contain function calls.

Pass Count

Stops program execution at a certain line number after a specified number of passes. Enter the number of passes.

Delphi decrements the pass count number each time the line containing the breakpoint is encountered. When the pass count equals 1, Delphi pauses program execution.

Because Delphi decrements pass counts with each pass, you can use them to determine which iteration of a loop fails. Set the pass count to the maximum loop count and run your program. When the program fails, you can calculate the number of loop iterations by examining the pass count number.

When you use pass counts with conditions, program execution pauses the nth time that the conditional expression is true. Delphi decrements the pass count only when the conditional expression is true.


See also

Using breakpoints

Breakpoint list window

Breakpoint list window tasks


Watch Properties dialog box

See also

Use the Watch Properties dialog box to add a watch or to change the properties of an existing watch. The watch appears in the Watch List window.

In addition to changing the properties of a watch, you can change the value of a watch expression. Use the Evaluate/Modify dialog box to change the value of a watch expression.

To open the Watch Properties dialog box, do one of the following:

Choose Add Watch At Cursor from the Code Editor SpeedMenu.

Choose Run|Add Watch.

Choose Add Watch from the Watch List SpeedMenu.

Right-click an existing watch in the Watch List window and choose Edit Watch from the Watch List SpeedMenu.

Dialog box options

Expression

Specifies the expression to watch. Enter or edit the expression you want to watch. Use the drop-down button to choose from a history of previously selected expressions.

Repeat Count

Specifies the repeat count when the watch expression represents a data element; specifies the number of elements in an array when the watch expression represents an array. Enter the repeat count number.

When you watch an array and specify the number of elements as a repeat count, the Watch List window displays the value of every element in the array.

Digits

Specifies the number of significant digits in a watch value that is a floating-point expression. Enter the number of digits.

Enabled

Enables or disables the watch. Disabling a watch hides the watch from the current program run. When you disable a watch, its settings remain defined, but Delphi does not evaluate the watch.

Disabling watches improves performance of the debugger because it does not monitor the watch as you step through or run your program. When you set a watch, it is enabled by default.

To format the display of a watch expression,

Select a radio button to specify the format of the display.

See Watch properties format types for complete information.


See also

Watch Properties dialog box


Watch Properties format types

See also

By default, the debugger displays the result of a watch in the format that matches the data type of the expression. For example, integer values are normally displayed in decimal form. If you select the Hexadecimal radio button in the Watch Properties dialog box for an integer type expression, Delphi changes the display format from decimal to hexadecimal.

Character

Shows special display characters for ASCII 0 to 31. By default, such characters are shown using the appropriate C escape sequences (/n, /t, and so on). This format type affects characters and strings.

String

Shows ASCII 0 to 31 as C escape sequences. Use only to modify memory dumps. This format type affects characters and strings.

Decimal

Shows integer values in decimal form, including those in data structures. This format type affects integers.

Hexadecimal

Shows integer values in hexadecimal with the 0x prefix, including those in data structures. This format type affects integers.

Ordinal

Shows integer values as ordinals.

Pointer

Shows pointers in segment:offset notation with additional information about the address pointed to. It tells you the region of memory in which the segment is located and the name of the variable at the offset address. This format type affects pointers.

Record

Shows both field names and values such as (X:1;Y:10;Z:5) instead of (1,10,5).

Default

Shows the result in the display format that matches the data type of the expression. This format type affects all.

Memory dump

Shows the size in bytes starting at the address of the indicated expression. By default, each byte displays two hex digits. Use the memory dump with the character, decimal, hexadecimal, and string options to change the byte formatting.


See also

Watching expressions

Watch list window

Watch list window tasks


Evaluate/Modify dialog box

See also

Use the Evaluate/Modify dialog box to evaluate or change the value of an existing expression or property. You can evaluate any valid language expression, except those that contain:

Local or static variables that are not accessible from the current execution point.

Function calls

Delphi enables you to change the values of variables and items in data structures during the course of a debugging session. You can test different error hypotheses and see how a section of code behaves under different circumstances by modifying the value of data items during a debugging session.

When you modify the value of a data item through the debugger, the modification is effective for that specific program run only. Changes you make through the Evaluate/Modify dialog box do not affect your source code or the compiled program. To make your change permanent, you must modify your source code in the Code Editor, then recompile your program.

Modifying values (especially pointer values and array indexes), can have undesirable effects because you can overwrite other variables and data structures. Use caution whenever you modify program values from the debugger.

Keep these points in mind when you modify program data values:

You can change individual variables or elements of arrays and data structures, but you cannot change the contents of an entire array or data structure.

The expression in the New Value box must evaluate to a result that is assignment-compatible with the variable you want to assign it to. A good rule of thumb is that if the assignment would cause a compile-time or run-time error, it is not a legal modification value.

You cannot directly modify untyped arguments passed into a function, but you can typecast them and then assign new values.

Formatting values

When you evaluate an expression, the current value of the expression is displayed in the Result field of the dialog box. If you need to, you can format the result by adding a comma and one or more format following specifiers to the end of the expression entered in the Expression edit box. See Evaluate/modify format specifiers for more information.

To open the Evaluate/Modify dialog box, do one of the following:

Choose Run|Evaluate/Modify.

Choose Evaluate/Modify from the Code Editor SpeedMenu.

Dialog box options

Expression

Specifies the variable, field, array, or object to evaulate or modify. Enter the variable, field, array, or object to evaluate or modify.

By default, the word at the cursor position in the Code Editor is placed in the Expression edit box. You can accept this expression, enter another one, or choose an expression from the history list of previously evaluated expressions.

Result

Displays the value of the item specified in the Expression text box after you choose Evaluate or Modify.

New value

Assigns a new value to the item specified in the Expression edit box. Enter a new value for the item if you want to change its value.

Evaluate

Evaluates the expression in the Expression edit box and displays its value in the Result edit box.

Modify

Changes the value of the expression in the Expression edit box using the value in the New Value edit box.


See also

Evaluating and modifying expressions


Evaluate/Modify format specifiers

See also

By default, the debugger displays the result in the format that matches the data type of the expression. Integer values, for example, are normally displayed in decimal form. To change the display format, type a comma (,) followed by a format specifier after the expression.

Example

Suppose the Expression box contains the integer value z and you want to display the result in hexadecimal:

1.	In the Expression box, type z,h.

2.	Choose Evaluate.

Format specifiers

The following table describes the Evaluate/Modify format specifiers.

Specifier	Types affected	Description

,C	Char, strings	Character. Shows characters for ASCII 0 to 31 in the Pascal #nn notation.

,S	Char, strings	String. Shows ASCII 0 to 31 in Pascal #nn notation.

,D	Integers	Decimal. Shows integer values in decimal form, including those in data structures.

,H or ,X	Integers	Hexadecimal. Shows integer values in hexadecimal with the $ prefix, including those in data structures.

,Fn	Floating point	Floating point. Shows n significant digits where n can be from 2 to 18. For example, to display the first four digits of a floating point value, type ,F4. If n is not specified, the default is 11.

,P	Pointers	Pointer. Shows pointers as 32-bit addresses with additional information about the address pointed to. It tells you the region of memory in which the pointer is located and, if applicable, the name of the variable at the offset address.

,R	Records, classes,

objects	Records/Classes/Objects. Shows both field names and values such as (X:1;Y:10;Z:5) instead of (1,10,5).

,nM	All	Memory dump. Shows n bytes, starting at the address of the indicated expression. For example, to display the first four bytes starting at the memory address, type ,4M. If n is not specified, it defaults to the size in bytes of the type of the variable. By default, each byte is displayed as two hex digits. Use memory dump with the C, D, H, and S format specifiers to change the byte formatting.


See also

Evaluate/modify dialog box


Debugger windows

See also

You can use the following windows to help you work in the integrated debugger:

Breakpoint List window	Shows all breakpoints set in the current source code and enables you to add, modify, and remove breakpoints

Watch List window	Shows all watches set in the current source code and enables you to add, modify, and remove watches

Call Stack window	Shows the sequence of function calls and the arguments passed to each function call


See also

Debugger menus

Debugger SpeedMenus


Debugger menus

See also

You can use the following menu items to help you work in the integrated debugger:

View menu

View|Using breakpoints

View|Call Stack

View|Watching expressions

Compile menu

Project|Compile

Run menu


See also

Debugger windows

Debugger SpeedMenus


Debugger SpeedMenus

See also

You can use the following SpeedMenus to help you work in the integrated debugger:

Breakpoint List SpeedMenu

Watch List context menu

Call Stack context menu

You can also use the following commands on the Code Editor SpeedMenu:

Code Editor SpeedMenu|Toggle Breakpoint

Code Editor SpeedMenu|Run To Cursor

Code Editor SpeedMenu|Evaluate/Modify

Code Editor SpeedMenu|Add Watch At Cursor


See also

Debugger windows

Debugger menus


See also

Debugger tasks


Breakpoint List window

See also

The Breakpoint List window displays a list of current breakpoints and lets you add, edit, delete, enable, and disable breakpoints.

A breakpoint is a place in your program where execution temporarily stops. When the debugger reaches a breakpoint, it pauses your program. You can specify any number of breakpoints in your code.

To display the Breakpoint List window, choose View|Breakpoints.

The following illustration shows a typical Breakpoint List window:

The previous illustration shows:

The file containing both breakpoints is FILEFORM.PAS.

Line numbers 52 and 74 in FILEFORM.PAS contain breakpoints.

The breakpoint at line 52 is a conditional breakpoint.

The pass count number for both breakpoints is zero.

The Breakpoint List window lists all breakpoints by their file name and line number. In addition, each breakpoint listing shows any condition and pass count associated with the breakpoint. If a breakpoint is either disabled or invalid, then the breakpoint listing is greyed in the Breakpoint List window.

Use the Breakpoint List window to view and maintain all your breakpoints, instead of searching through your source files. You can view or edit the code at any breakpoint location by using the commands on the Breakpoint List window SpeedMenu.


See also

Debugger windows

About the Debugger

Using breakpoints

Breakpoint List window tasks


Watch List window

See also

The Watch List window displays the current value of your watches and lets you add, edit, delete, enable, and disable watches.

A watch monitors the changing values of variables or expressions during your program run. As your program runs, the value of the watch changes as your program updates the values of the variables contained in the watch expression.

To display the Watch List window, choose View|Watches.

The following illustration shows a typical Watch List window:

The previous illustration shows:

The value of PathLabel.Caption is 'C:\DELPHI'.

The value of FileLabel.Caption is 'fileform.dfm'.

If the execution point moves to a location where any of the variables in the watch expression are undefined, then the entire watch expression becomes undefined. If the execution point returns to a location where the watch expression can be evaluated, then the Watch List window again displays the current value of the watch expression.


See also

Debugger windows

About the Debugger

Watching expressions

Watch List window tasks


Call Stack window

See also

The Call Stack window displays the function calls that brought you to your current program location and the arguments passed to each function call.

To display the Call Stack window, choose View|Call Stack.

The following illustration shows a typical Call Stack window:

The top of the Call Stack window lists the last function called by your program. Below this is the listing for the previously called function. The listing continues, with the first function called in your program located at the bottom of the list. Each function listing in the window is followed by the arguments that were passed when the call was made.

Only functions in the currently loaded module are listed in the Call Stack window.


See also

Debugger windows

About the Debugger

Viewing function calls


Debugger tasks

See also

The following topics provide an overview of debugger tasks.

General debugging tasks

Generating debugging information

Generating Generating debugging information

Specifying program arguments

Starting a debugging session

Customizing the breakpoint and execution point colors

Controlling program execution

Stepping through code

Working with breakpoints

Setting Using breakpoints

Locating Using breakpoints

Disabling and enabling Using breakpoints

Deleting Using breakpoints

Modifying breakpoint properties

Working with watches

Watching expressions

Disabling a watch

Deleting a watch

Examining and modifying data values

Evaluating and modifying expressions

Working with function calls

Viewing function calls

Handling exceptions

Handling exceptions in the debugger


See also

About the Debugger


Breakpoint list window tasks

See also

Use the Breakpoint List window to perform any of the following tasks:

Setting Using breakpoints

Locating Using breakpoints

Disabling and enabling Using breakpoints

Deleting Using breakpoints

Modifying breakpoint properties


See also

Using breakpoints

Breakpoint list window

Debugger tasks


Watch list window tasks

See also

Use the Watch List window to perform any of the following tasks:

Watching expressions

Disabling a watch

Deleting a watch


See also

Watching expressions

Watch list window


Decision Query Editor dialog box

The Decision Query Editor dialog box defines queries for the active decision query component. This dialog box specifies the database, tables, available fields, dimensions, and summaries for decision cubes bound to the decision query. It also displays the defined query in ANSI-92 SQL syntax.

To display the Decision Query Editor dialog box, apply a decision query component to a form, then right-click and choose Decision Query Editor from the menu.

Dialog box options

Dimensions/Summaries tab

Specifies the database, tables, available fields, dimensions, and summaries for decision cubes bound to the decision query.

SQL Query tab

Displays the defined query in ANSI-92 SQL syntax. You can edit the query by editing the ANSI-92 SQL statements.

Query Builder button

Displays the Visual Query Builder to select and join data tables.


Dimensions/Summaries tab (Decision Query editor)

Use this tab of the Decision Query Editor dialog box to define queries for the active decision query component. This tab specifies the database, tables, available fields, dimensions, and summaries for decision cubes bound to the decision query.

To display the Decision Query Editor dialog box, apply a decision query component to a form, then right-click and choose Decision Query Editor from the menu. Then, click the Dimension/Summaries tab if it isn't already visible.

To display a text version of the defined query, click the SQL Query tab.

Dialog box options

List Of Available Fields

Fields available for use in the decision query. If you're using more than one table and need to create a join, you can click the Query Builder button to launch the Visual Query Builder.

All Fields/Query Fields button

Toggles between displaying all fields in the selected table and only fields selected by the active query.

Dimensions

Lists fields selected as decision cube dimensions. To add a field to the Dimensions list, select a field in the List Of Available Fields, then click the right-arrow. To remove a field from the Dimensions list, select it in the list, then click the left-arrow.

Summaries

Lists fields selected as decision cube summaries. To add a field to the Summaries list, select a field in the List Of Available Fields, then click the right-arrow. To remove a field from the Summaries list, select it in the list, then click the left-arrow.

Table

Shows the active table from those included in the active database. You can select a different table from this dropdown list.

Database

Shows the active database from those included in the query. You can select a different database from this dropdown list.


SQL Query tab (Decision Query editor)

Use this tab of the Decision Query Editor dialog box to display SQL queries for the active decision query component.

If a query hasn't yet been defined, you can enter one directly in ANSI-92 SQL. Or, you can define it visually by selecting a database, tables, and fields on the Dimensions/Summaries tab.

To display the Decision Query Editor dialog box, apply a decision query component to a form, then right-click and choose Decision Query Editor from the menu. Then, click the SQL Query tab.

Dialog box options

Query Text

Displays SQL statements that define the current decision query. The defined query apears in ANSI-92 SQL syntax. You can edit the query directly by editing the ANSI-92 SQL statements; they are automatically converted to and from any appropriate dialect used to communicate with the server.

Edit Query/Edit Done button

When the Edit Query button is active, text appears in the Query Text window. You can type over it to edit it.

When you begin typing, button text changes to Edit Done. Click the button when your edit is complete. You can click Cancel Edit to restore the original query text.

Cancel Edit button

Cancels the current text edit and restores the original query.


Decision Cube Editor dialog box

The Decision Cube Editor dialog box defines active dimensions and summaries for the active decision cube component. These settings are reflected in decision pivots bound to the decision cube.

To display the Decision Cube Editor dialog box, apply a decision cube component to a form, bind it to a decision query, then right-click the decision cube and choose Decision Cube Editor from the menu.

Dialog box options

Dimension Settings tab

Specifies the display name, type, active type, format, grouping, and initial value for fields supplied to the decision cube from the decision query component.

Memory Control tab

Displays the following settings for memory protection:

Decision cube dimension, summary, and cell maximums

Designer data display choices


Dimension Settings tab (Decision Cube editor)

Use this tab of the Decision Cube Editor dialog box to specify the display name, type, active type, format, grouping, and initial value for fields supplied to the decision cube from the decision query component.

To display the Decision Cube Editor dialog box, apply a decision cube component to a form, bind it to a decision query, then right-click the decision cube and choose Decision Cube Editor from the menu. Then, click the Dimension Settings tab if it isn't already visible.

Dialog box options

Available Fields

Fields supplied to the decision cube component by the decision query component. When a field in the list is highlighted, its settings appear in the text boxes and lists on the right side of the dialog box.

Display Name

The name to appear in decision pivot, decision grid, and decision graph labels for the highlighted field.

Type

Whether the highlighted field is a dimension or a summary (for information only, not editable).

Active Type

When the information for the highlighted field is loaded: As Needed, when required for display; Active, all the time; Inactive, never.

Format

The Delphi format string that describes how to display values for the highlighted field.

Grouping

Whether to display all values or ranges of values. Use None to display all values. Use Year, Quarter, or Month to display a range of dates. Use Single Value for a single-dimension display. If custom ranges have been added by an application developer, they also appear in the list.

Initial Value

The starting value for a date or custom range, or the single value to display.


Memory Control tab (Decision Cube editor)

Use this tab of the Decision Cube Editor dialog box to change the following settings for memory protection:

Decision cube dimension, summary, and cell maximums

Designer data display choices

To display the Decision Cube Editor dialog box, apply a decision cube component to a form, bind it to a decision query, then right-click the decision cube and choose Decision Cube Editor from the menu. Then, click the Memory Control tab.

Dialog box options

Cube Maximums

Sets maximums and displays current values for decision cube dimensions, summaries, and cells.

Maximum

Sets the maximum allowable dimensions, summaries, and cells for the selected decision cube. The lower the number, the less memory is used

Current

The current number of dimensions, summaries, and cells in use by the decision cube (for information only, not editable).

Active+Needed

The number of dimensions and summaries that have Active Type set to Active or As Needed on the Dimension Settings tab of the Decision Cube Editor dialog box, and the number of cells required to display them (for information only, not editable).

Active

The number of dimensions and summaries that have Active Type set to Active on the Dimension Settings tab of the Decision Cube Editor dialog box, and the number of cells required to display them (for information only, not editable).

Get Cell Counts button

When clicked, runs a query to estimate the number of cells used.

Designer Data Options

Saves time and memory by displaying only the specified data at design time.

Display Dimension Names

When checked, displays only dimension and summary names at design time. No values appear.

Display Names And Values

When checked, displays dimension and summary names and values at design time. No totals appear.

Display Names, Values, And Totals

When checked, displays dimension and summary names, values, and totals at design time.

Run Time Display Only

When checked, displays dimension and summary names, values, and totals only at run time. None of this data appears at design time.


Code Editor window

See also

The Code Editor window contains one or more Code Editor pages. The Code Editor window cannot be empty -- once you close the last page in the Code Editor window, the window is closed.

You can open multiple files in one Code Editor window. Each file opens on a new page of the Code Editor, and each page is represented by a tab at the top of the window. For example, when you open a project, it becomes the first tab in the window. Any other files that you open, such as unit files, become subsequent tabs in the window.

You can open a copy of any editor page, which opens a separate window.

To open a Code Editor window, you can do one of the following:

Open a file.

Choose View|New Edit Window.

The New Window command opens a copy of the current page in the Code Editor.

If you have modified the code and not saved the changes, Delphi opens the Save As dialog box, where you can enter a file name.


See also

Adding components to a form

Working with the Code editor


Behind the scenes in the Code Editor

See also

When you add a component to a form, Delphi generates an instance variable, or field, for the component and adds it to the form's type declaration. For example, look at the following code sample, adding a pushbutton component to a blank form.

type

  TForm1 = class(TForm)

    Button1: TButton;

  end;

Adding the pushbutton changes the form's type declaration (Tform1 = class (Tform)) by adding the field for the button itself (Button1: Tbutton;). You can view similar code being added to the Code Editor, either in your current project or in a new project.

To view code being added in the Code Editor,

1.	Drag the form's title bar until you can see the entire Code Editor.

2.	Scroll in the Code Editor until the type declaration part is visible.

3.	Add a component to the form while watching what happens in the Code Editor.

Note:	Do not edit any code that Delphi generates. Edit only code that you create.


See also

Adding components to a form

Working with the Code editor


Getting Help in the Code Editor

See also

Context-sensitive Help is available from nearly every portion of the Code Editor. The context is determined by the current position of the cursor.

To get context-sensitive Help from the Code Editor window, do one of the following:

Place the cursor on the property, event, method, property, procedure or type for which you want Help, then press F1.

Right-click the Code Editor, then choose Topic Search from the SpeedMenu.

If Help is not available for the specific topic you selected, Help displays a message reading, "Help Topic Not Found." If this message appears, you have the three options:

Return to the main Help screen.

Select another topic for Help to search.

Return to a previously viewed topic.


See also

Working with the Code editor


Viewing pages in the Code Editor window

See also

When a page of the Code Editor is displayed, you can scroll through all the data it contains, not just particular sections of your code.

To view a page in the Code Editor, do one of the following,

If the Code Editor is already the active window, click the tab corresponding to the page you want to view.

Choose View|Units

Choose View Unit from the Project Manager SpeedMenu.

Click the View Unit button on the Project Manager SpeedBar.


See also

Project Manager

Working with the Code editor


Code Editor SpeedMenu

See also

The Code Editor SpeedMenu contains commands for navigating, modifying, and debugging your source code. This menu is unique to the Code Editor, and the commands contained in the menu pertain only to the Code Editor.

The SpeedMenu commands are listed below. To view detailed information on a Code Editor SpeedMenu command, click that command in the list below:

Close Page

Open File At CursorNew Edit Window

Browse Symbol At Cursor

Topic SearchAdd to Interface

Toggle Breakpoint

Run to Cursor

Evaluate/Modify

Add Watch at Cursor

View As Form

Read OnlyMessageViewProperties

To open the Code Editor SpeedMenu, do one of the following:

Right-click anywhere in the Code Editor window.

Press Alt+F10 when the Code Editor window is active.


See also

Working with the Code editor


Close Page (Code Editor SpeedMenu)

Code Editor SpeedMenu

Choose Close Page from the Code Editor SpeedMenu to close the current page in the Code Editor window.

If you have modified code, not saved the changes, and this is the last page open in a file, Delphi opens the Save As dialog box, where you can enter a new file name.

If you are closing the last page in the project and have not saved it yet, Delphi opens the Save As dialog box, where you can enter a name for the project.


Open File At Cursor (Code Editor SpeedMenu)

Code Editor SpeedMenu

Choose Open File At Cursor from the Code Editor SpeedMenu to open the file at the current cursor position.

Delphi searches for files with the default extension of .PAS, unless another file extension is explicitly specified. Similarly, Delphi uses the directory settings for unit and include files specified in the Directories/Conditionals page of the Options|Project dialog box.

To change directory settings for unit and include files,

1.	From the main Delphi menu, choose Options|Project.

2.	Click the Directories/Conditionals page.

3.	Set unit and include directories as you want.

4.	Choose OK to put your choices into effect.


New Edit Window (Code Editor SpeedMenu)

Choose View|New Edit Window to open a new Code Editor that contains a copy of the active page from the original Code Editor.

Any changes you make to either the original or the copy are reflected in both files.

So that you can distinguish between the windows, the caption in the original window is postfixed with a 1, the first copy with a 2, the second copy with a 3, and so on.


Browse Symbol At Cursor (Code Editor SpeedMenu)

See also	Code Editor SpeedMenu

Choose Browse Symbol At Cursor from the Code Editor SpeedMenu to open the Symbol Inspection window for the highlighted symbol.

The symbol can be any object, unit, or variable symbol defined in your source code (not only in the current file, but also in any source file compiled and linked as part of the same project).

To browse symbols, you need to compile with the Local Symbols and Symbol Information options on the Compiler Options page of the Project dialog box enabled.


See also

ObjectBrowser


Topic Search (Code Editor SpeedMenu)

Code Editor SpeedMenu

Choose Topic Search from the Code Editor SpeedMenu to display a Help window for the word or token at the cursor in the Code Editor.

If no Help topic exists, the Borland Search dialog box is displayed, with the closest match highlighted.


Message View (Code Editor SpeedMenu)

Code Editor SpeedMenuChoose Message View to toggle the message window at the bottom of the code editor. When you compile an application, errors or warning messages are displayed in the message window; and when you conduct a search, search results are displayed in the message window.

The message window SpeedMenu commands are listed below.

View Source	Scrolls the Code Editor to the location of the error message or search result that is selected in the message window.

Edit Source	Scrolls the Code Editor to the location of the the error message or search result that is selected in the message window, and makes the Code Editor active.

Clear Compiler Messages	Clears only compiler messages from the

Clear Search Results	Clears only search result messages from the message window.


Toggle Breakpoint (Code Editor SpeedMenu)

See also	Code Editor SpeedMenu

Choose Toggle Breakpoint from the Code Editor SpeedMenu to toggle a breakpoint on and off at the current cursor position.

If no breakpoint is set when you choose this command, Delphi sets one and turns it on. If a breakpoint is already set, choosing this command toggles the breakpoint off.

To modify breakpoint properties,

Right-click an existing breakpoint in the Breakpoint List window and choose Edit Breakpoint.


See also

Setting Using breakpoints

Using breakpoints

The Delphi Debugger


Run To Cursor (Code Editor SpeedMenu)

See also	Code Editor SpeedMenu

Choose Run To Cursor to run the loaded program up to the location of the cursor in the Module window.

When you run to the cursor, your program is executed at full speed, then pauses and places the execution point on the line of code containing the cursor.

You can use Run To Cursor to run your program and pause before the location of a suspected problem. You can then use Run|Step Over or Run|Trace Into to control the execution of individual lines of code.

An alternative way to perform this command is:

Choose Run|Run To Cursor.


See also

Controlling program execution

About the Debugger


Evaluate/Modify (Code Editor SpeedMenu)

See also	Code Editor SpeedMenu

The Evaluate/Modify command opens the Evaluate/Modify dialog box, which lets you evaluate or change the value of an existing expression.

An alternate way to perform this command is:

Choose Run|Evaluate/Modify.


See also

Evaluating and modifying expressions

The Delphi Debugger


Add Watch At Cursor (Code Editor SpeedMenu)

See also	Code Editor SpeedMenu

The Add Watch At Cursor command opens the Watch Properties dialog box, where you can create and modify watches. After you create a watch, use the Watch List window to display and manage the current list of watches.

Alternate ways to perform this command are:

Choose Run|Add Watch from the Code Editor SpeedMenu.

Choose Add Watch from the Watch List SpeedMenu.

Right-click an existing watch in the Watch List window and choose Edit Watch from the Watch List SpeedMenu.


See also

Watching expressions

Watching expressions

About the debugger


Read Only (Code Editor SpeedMenu)

Code Editor SpeedMenu

Choose Read Only from the Code Editor SpeedMenu to make the current open file read only. When a file is read only, you cannot make any changes to the file.

When you mark a file as read only this command is checked on the Code Editor SpeedMenu and "Read only" is displayed in the Code Editor status line.


Code Editor Control menu

See also

The Code Editor's Control-menu box is on the far left of the Code Editor window. Click it once or press Alt+Spacebar to display the Control menu. The commands that appear on this menu affect the Code Editor window.

The commands available on the Control menu are:

Restore

Move

Size

Minimize

Maximize

Close


See also

Working with the Code editor


Control | Restore

Code Editor Control Menu

Choose Restore to return the Code Editor window to its previously displayed size and position (except the maximized or minimized sizes).

The Restore command is available only if the Code Editor window is maximized or minimized.

Shortcut

Click the upward/downward double triangle in the upper right corner of the Code Editor.


Control | Move

Code Editor Control Menu

Choose Move to change the position of the Code Editor window using keyboard commands, rather than by dragging it with the mouse.

To move the Code Editor window,

1.	Choose Control|Move.

2.	Use your keyboard’s arrow keys to move the window to the location you want.

3.	Press Enter.

Note:	The Move command is available only if the Code Editor window is not maximized.

Shortcut

Drag the Code Editor’s title bar.


Control | Size

Code Editor Control Menu

Choose Size to change the size of the Code Editor window using the keyboard, rather than by dragging the window borders.

To resize the Code Editor window,

1.	Choose Control|Size.

2.	Use your keyboard’s arrow keys to move the window borders until the window is the size you want.

3.	Press Enter.

Note:	The Size command is available only if the Code Editor window is not maximized.

Shortcut

Drag the Code Editor’s window borders. Use the top or bottom border to resize the window vertically, use the left or right border to resize the window horizontally, or use any border corner to resize the window diagonally.


Control | Minimize

Code Editor Control Menu

Choose Minimize to shrink the Code Editor window, together with all other Delphi windows, into a single Delphi icon.

The Minimize command is available only if the Code Editor is not already minimized.

Shortcut

Click the downward-pointing triangle in the upper right corner of the Code Editor.


Control | Maximize

Code Editor Control Menu

Choose Maximize to enlarge the Code Editor window to fill your entire screen.

The Maximize command is available only if the Code Editor window is not already maximized.

Shortcut

Click the upward-pointing triangle in the upper right corner of the Code Editor.


Control | Close

Code Editor Control Menu

Choose Close to close all Code Editor pages and the project. If you have modified code and have not saved the changes, Delphi opens the Save As dialog box, where you can enter a new file name.

This command also closes the current project. If you have not saved the project, Delphi opens the Save As dialog box, where you can enter the name of the project.

Shortcuts

Press Alt+F4.

Double-click the Code Editor Control-menu box.


Write Block To File dialog box

See also

This dialog box enables you to specify the filename and location of an operating system file in which you want to write a block of text you have selected in the Code Editor Window.

When using default key mapping, access this dialog box with: Ctrl+K+W


See also

Read File As Block dialog box


Read File As Block dialog box

See also

This dialog box enables you to specify the filename and location of an operating system file containing a block of Object Pascal source code which you want to insert in the Code Editor Window at the current cursor position.

When using default key mapping, access this dialog box with: Ctrl+K+R


See also

Write Block To File dialog box


ImageList Editor

Use the ImageList Editor at design time to add bitmaps and icons to a TImageList component. To display the ImageList editor, double-click a TImageList component or right-click it and choose ImageList Editor

Selected Image

Displays information about the image selected in Images. Controls in this group are grayed if there are no images available or the selected image was added in a previous use of the editor.

Transparent Color

Determines which color is used to create a mask for drawing the image transparently. The default transparent color is the color of the bitmap's left-most pixel in the bottom line. Transparent Color is set to clNone for an icon image (icons are already masked).

Fill Color

Used when an image is smaller than the TImageList component to specify what color the empty area of the image is filled with when it is cropped or centered. Fill Color is set to clNone for an icon image.

Options

Specifies how to adjust a newly added bitmap image whose width or height don't match the imagelist's width or height. (Disabled for icons.)

Setting	Description

Crop	Displays the section of the image beginning at the top-left, extending the imagelist width and height towards the bottom-right (default)

Stretch	Causes the entire image to stretch so that it fits the imagelist width and height

Center	Centers the image within the imagelist width and height. If the image width or height is larger than the imagelist width or height, the image may be clipped.

Images

Contains a preview list view of all the images in the imagelist, and controls for adding or deleting images from the list. Each image is displayed within a 24x24 area for easier viewing of multiple images. Beneath each image is a caption that indicates the zero-based position of the image within the imagelist. You can edit the caption to change an images position in the list or drag the image to its new position.

Add

Displays the Add Images dialog box, which lets you select one or more bitmaps or icons to add to the imagelist. The images then appear highlighted in the preview list view and their captions are assigned sequential values in the imagelist.

If a bitmap is larger than the imagelist width or height by even increments, a prompt appears asking whether the ImageList editor should divide the bitmap into several images. This is useful for toolbar bitmaps, which are usually composed of several small images in a sequence and stored as one larger bitmap.

Delete

Removes the selected images from the imagelist. All images left after clicking Delete will be repositioned to be a contiguous zero-based list.

Clear

Removes all images from the imagelist.


Responding to tab-set changes

Example

When the user selects a tab in a tab-set control by clicking a tab or using the keyboard, the tab set generates an OnClick event. Any other controls that depend on the setting of the tab set need to have their values updated in response to those click events.

For example, with a tab set that contains tabs for each valid disk drive, a click on a different tab indicates a change of drive selection. Other controls, such as directory lists, need to respond to the change.

To respond to changes in a tab set, attach an event handler to the tab set's OnClick event.


Example

The following code, attached to the OnClick event of a tab set named DriveTabSet, sets the Drive property in a directory-outline control to the first letter of the clicked tab:

procedure TFMForm.DriveTabSetClick(Sender: TObject);
begin
  with DriveTabSet do
    DirectoryOutline.Drive := Tabs[TabIndex][1];
end;


Responding to outline changes

Example

When the user selects an item in an outline by clicking it or using an arrow key, the outline generates a click. Any controls that depend on the currently selected item in the outline need to update themselves in response to those clicks.

For example, in a component such as the directory outline, a click probably indicates a change in the current directory. Related controls, such as file lists, need to respond to this change. However, it is possible that the click was on the directory already selected. Instead of handling the OnClick event, it is more useful to handle the OnChange event, which indicates that something in the directory outline has changed.

To respond to changes in an outline, attach a handler to the outline's OnChange event.


Example

The following code updates both a file list box and a status-bar panel to reflect the current directory in a directory outline component every time the directory outline changes:

procedure TFMForm.DirectoryOutlineChange(Sender: TObject);
begin
  FileList.Directory := DirectoryOutline.Directory;
  DirectoryPanel.Caption := DirectoryOutline.Directory;
end;


Responding to list box changes

Example

When the user clicks an item in a list box or uses an arrow key to move to an item, that item becomes the selected item, and the list box generates an OnChange event. The application can handle the change and update any dependent controls.

To respond to changes in a list box, attach an event handler to the list box's OnChange event.

For example, a file list box can have an associated panel that displays information about the selected file or files.


Example

The following event handler for a file list box's OnChange event updates a status-bar panel with the name and size of the file currently selected in a file list box.

Note:	GetFileSize is a function in the FMXUtils unit installed with the file manager sample application.

procedure TFMForm.FileListChange(Sender: TObject);
var
  TheFileName: string;
begin
  with FileList do
  begin
    if ItemIndex >= 0 then	{ is there a selected item? }
    begin
      TheFileName := FileName;	{ get the file name }
      FilePanel.Caption := TheFileName + ',

        { set panel caption to the name }
        + IntToStr(GetFileSize(TheFileName)) + ' bytes';

        { and size }
    end

    else FilePanel.Caption := '';	{ blank panel if none selected }
  end;
end;


Creating an owner-draw control

Windows list-box and combo-box controls have a style available called "owner draw," which means that instead of using Windows' standard method of drawing text for each item in the control, the control's owner (generally the form) draws each item at run-time. The most common application for owner-draw controls is to provide graphics instead of, or in addition to, text for items.

The following Delphi components support the owner draw style:

List-box

Combo-box

Status Panel	Header Section

Owner-draw list box and combo box controls contain lists of items. Those lists are lists of strings, which Windows displays as text. Delphi enables you to associate an object with each item in a list, and gives you the chance to use that object when drawing items.

To create an owner-draw control in Delphi follow the following three steps:

1.	Set the owner-draw style

2.	If the control is a list box or combo box, add graphical objects to a string list

3.	Draw owner-draw items


Setting the owner-draw style

Each control that has an owner-draw variant has a property called Style. Style determines whether the control uses the default drawing (called the "standard" style) or owner drawing.

For list boxes and combo boxes, there is also a choice of owner-draw styles, called fixed and variable, as the following table describes. Owner-draw status panels, header sections, and grids are always fixed.

Owner-draw style	Meaning	Examples
Fixed	Each item is the same height, If the control is a list box or combo box,  that height is determined by the ItemHeight property.	lbOwnerDrawFixed, csOwnerDrawFixed; psOwnerDraw; hsOwnerDraw
Variable	Each item might have a different height, deter-mined by the data at run time.	lbOwnerDrawVariable, csOwnerDrawVariable
To set the owner-draw style,

Set Style property for the owner-draw component.  With Owner draw grids, use the DefaultDrawing property instead.


Adding graphical objects to a string list

See also

Every Delphi string list can hold objects in addition to strings. That is, in addition to its indexed Strings property, which contains strings, a string list also has an Objects property.

Once you have graphical images in an application, you can associate them with the Objects property in a string list. You can either add the objects at the same time as the strings, or associate objects with already-added strings. If you have all the needed data available, you should generally add strings and objects together.

To add a graphical object to an existing string in a string list,

Assign the object to the Objects property at the same index.


See also

Adding objects to a string list


Adding images to an application

An image control is a non-visual component that contains a graphical image, such as a bitmap. You use image controls to display graphical images on a form, but you can also use them to hold hidden images that you will use in your application. For example, you can store bitmaps for owner-draw controls in hidden image controls.

To add an image to an application,

1.	Add an image control to the main form.

2.	Set its Visible property to False.

3.	Set the Picture property to display the bitmapped image.

Note:	The image will be visible on the form but when you run the application they will be invisible.


Drawing owner-draw items

When you set a control's style to owner draw, Windows no longer draws the control on the screen. Instead, it generates events for each visible item in the control. Your application handles the events to draw the items.

To draw the items in an owner-draw control,

1.	Size the item.

If the owner-draw items are all the same size (for example, with a list box style of lsOwnerDrawFixed), you do not need to do this step.

2.	Draw the item.


Sizing owner-draw items

Before giving your application the chance to draw each item in a variable owner-draw control, Windows generates a measure-item event. The measure-item event tells the application where the item will appear on the control.

Delphi determines what size the item will probably be (generally just large enough to display the item's text in the current font). Your application can handle the event and change the rectangle Delphi chose. For example, if you plan to substitute a bitmap for the item's text, you would change the rectangle to be the size of the bitmap. If you want the bitmap and text, you adjust the rectangle to be big enough for both.

To change the size of an owner-draw item, attach an event handler to the OnMeasureItemevent in the owner-draw control.

The sizing event has two important parameters: The index number of the item and the size of that item. That size is variable: the application can make it either smaller or larger. The positions of subsequent items depends on the size of preceding items.

For example, in a variable owner-draw list box, if the application sets the height of the first item to five pixels, the second item starts at the sixth pixel down from the top, If the application then sets the height of the second item to ten pixels, the third item starts at the sixteenth pixel from the top, and so on.


Drawing each owner-draw item

When an application needs to draw or redraw an owner-draw control, Windows generates draw-item events for each visible item in the control.

To draw each item in an owner-draw control, attach an event handler to the draw-item event for that control. The events for owner drawing are:

OnDrawCell

OnDrawColumnCell

OnDrawItem

OnDrawPanel	OnDrawSection

The draw-item event contains

parameters indicating the index of the item to draw

the rectangle in which to draw

information about the state of the item (such as whether the item has focus).

The application handles each event by rendering the appropriate item in the given rectangle.

Note:	Most draw-item events do not pass a canvas object as a parameter; you normally use the canvas of the control being drawn.


Manipulating files

There are several common file operations built into Object Pascal's run-time library. The procedures and functions for working with files operate at a high level: You specify the name of the file you want to work on, and the routine makes the necessary calls to the operating system for you.

Previous versions of the Pascal language performed similar operations on files themselves, rather than on file names. That is, you had to locate a file and assign it to a file variable before you could, for example, rename the file. By operating at the higher level, Object Pascal reduces your coding burden and streamlines your applications. The lower-level functions are still available, but you should not need them as often.

Choose a topic for more information.

Deleting a file

Renaming a file

Changing a file's attributes


Deleting a file

Example

Deleting a file erases the file from the disk and removes the entry from the disk's directory. There is no corresponding operation to restore a deleted file, so applications should generally allow users to confirm deletions of files.

To delete a file, pass the name of the file to the DeleteFile function. DeleteFile returns True if it deleted the file and False if it did not (for example, if the file did not exist or if it was read-only).


Example

The following code handles a click on a File|Delete menu item by deleting the selected file in a file list box, then updating the list so it reflects the deletion.

procedure TFMForm.Delete1Click(Sender: TObject);
begin
  with FileList do
    if DeleteFile(FileName) then Update;
end;


Changing file attributes

See also	Example

Every file has various attributes stored by the operating system as bitmapped flags. File attributes include such items as whether a file is read-only or a hidden file.

Changing a file's attributes requires three steps:

1.	Reading file attributes.

2.	Changing individual file attributes.

3.	Setting file attributes.

You can use the reading and setting operations independently, if you only want to determine a file's attributes, or if you want to set an attribute regardless of previous settings. To change attributes based on their previous settings, however, you need to read the existing attributes, modify them, and write the modified attributes.

Reading file attributes

Operating systems store file attributes in various ways, generally as bitmapped flags.

To read a file's attributes, pass the file name to the FileGetAttrfunction. The return value is a group of bitmapped file attributes, of type Word.

Changing individual file attributes

Because Delphi represents file attributes in a set, you can use normal logical operators to manipulate the individual attributes. Each attribute has a mnemonic name defined in the SysUtils unit.

For example, to set a file's read-only attribute, you would do the following:

Attributes := Attributes or faReadOnly;

You can also set or clear several attributes at once. For example, the clear both the system-file and hidden attributes:

Attributes := Attributes and not (faSysFile or faHidden);

Setting file attributes

Delphi enables you to set the attributes for any file at any time.

To set a file's attributes, pass the name of the file and the attributes you want to the FileSetAttr function.


See also

FindFirst function


Example

The following code reads a file's attributes into a set variable, sets the check boxes in a file-attribute dialog box to represent the current attributes, then executes the dialog box. If the user changes and accepts any dialog box settings, the code sets the file attributes to match the changed settings:

procedure TFMForm.Properties1Click(Sender: TObject);
var
  Attributes, NewAttributes: Word;
begin
  with FileAttrForm do
  begin
    FileDirName.Caption := FileList.Items[FileList.ItemIndex];
    { set box caption }
    PathName.Caption := FileList.Directory;
    { show directory name }
    ChangeDate.Caption :=
      DateTimeToStr(FileDateTime(FileList.FileName));
    Attributes := FileGetAttr(FileDirName.Caption);
    { read file attributes }
    ReadOnly.Checked := (Attributes and faReadOnly) = faReadOnly;
    Archive.Checked := (Attributes and faArchive) = faArchive;
    System.Checked := (Attributes and faSysFile) = faSysFile;
    Hidden.Checked := (Attributes and faHidden) = faHidden;
    if ShowModal <> id_Cancel then	{ execute dialog box }
    begin
      NewAttributes := Attributes;
      { start with original attributes }
      if ReadOnly.Checked then
        NewAttributes := NewAttributes or faReadOnly
      else
        NewAttributes := NewAttributes and not faReadOnly;
      if Archive.Checked then
        NewAttributes := NewAttributes or faArchive
      else
        NewAttributes := NewAttributes and not faArchive;
      if System.Checked then
        NewAttributes := NewAttributes or faSysFile
      else
        NewAttributes := NewAttributes and not faSysFile;
      if Hidden.Checked then
        NewAttributes := NewAttributes or faHidden
      else
        NewAttributes := NewAttributes and not faHidden;
      if NewAttributes <> Attributes then { if anything changed... }
        FileSetAttr(FileDirName.Caption, NewAttributes);
         { ...write the new values }
    end;
  end;
end;


Reusing forms as DLLs

See also

When you create a form that you want to use in multiple applications, especially when the applications are not Delphi applications, you can build the form into a dynamic-link library (DLL). A DLL is a compiled executable file, so applications written with tools other than Delphi can call them. For example, you can call a DLL from applications created with C++, Paradox, or dBASE.

DLLs are standalone files that contain the overhead of the component library (about 100K). You can minimize this overhead by compiling several forms into a single DLL. For example, suppose you have a suite of applications that all use the same dialog boxes for checking passwords, displaying shared data, or updating status information. You can compile all of these dialog boxes into a single DLL, allowing them to share the component-library overhead.

Building a form into a DLL takes three steps:

1.	Declaring interface routines

2.	Modifying Project file

3.	Accessing routines stored in DLLs


See also

Writing DLLs


Declaring an interface routine
Declaring interface routines

See also	Example

When you are compiling an application into a DLL, interface routines enable you to access the routine in the DLL from an outside application.

Adding an interface-routine declaration involves declaring a procedure or function in the interface section of the unit to be compiled into the DLL, and following that declaration with the export directive.

When writing interface routines that will be called from languages other than Object Pascal, you must declare parameters and return values using types that are available in the calling language. For example, you should pass strings as null-terminated arrays of characters (the Object Pascal type PChar) rather than Object Pascal's native string type.

After declaring an interface routine, you can define the routine in the implementation section of the unit.


See also

Accessing routines stored in DLLs

Functions

Interfaces

Procedures

Writing DLLs


Example

The following example declares the function GetPassword as a interface routine. The exports section includes the GetPassword routine name to ensure that the function is successfully exported.

unit PassForm;

interface

uses

  SysUtils, Windows, Messages, Classes, Graphics,

  Forms, Controls, Forms, Dialogs, StdCtrls, Buttons;

type

  TPasswordForm = class(TForm)

  ...	{ various declarations go here }

  end;

var

  PasswordForm: TPasswordForm;

function GetPassword(APassword: PChar; hAppHandle: THandle): WordBool;

exports GetPassword;

implementation

function GetPassword(APassword: PChar; hAppHandle: THandle): WordBool;

begin

  Application.Handle := hAppHandle; { Associate the DLL’s Application handle with the
               loading Application’s handle. }

  PasswordForm := TPasswordForm.Create(Application);

  try

    if PasswordForm.ShowModal = mrOK then

    begin

      {Code to validate entered password values here}

      Result := True;

    end;

  finally

    PasswordForm.Free;

  end;

end;

end.


Compiling a project into a DLL

See also	Example

When you are compiling a project into a DLL, you need to make the following edits in the project file:

1.	Change the reserved word program in the first line of the file to library.

2.	Remove the Forms unit from the project's uses clause.

3.	Remove all lines of code between the begin and end at the bottom of the file.

4.	Below the uses clause, and before the begin..end block, add the reserved word exports, followed by the names of the interface routines and a semicolon.

Delphi will not create the list of interface routines to be exported.

After these modifications, when you compile the project, it produces a DLL instead of an application. Applications can now call the DLL to open the wrapped dialog box.


See also

Declaring interface routines

Writing DLLs


Example

The following example shows a typical project file before and after modification:

program Password;

uses Forms,

     PassForm in 'PASSFORM.PAS' {PasswordForm};

{$R *.RES}

begin

  Application.CreateForm(TPasswordForm, PasswordForm);

  Application.Run;

end.

After modifications:

library Password;	{ 1. reserved word changed }

uses	{ 2. removed Forms, }

     PassForm in 'PASSFORM.PAS' {PasswordForm};

exports

  GetPassword;	{ 4. add exports clause }

{$R *.RES}

begin	{ 3. remove code from main block }

end.


Saving a form under a different name

See also

You choose File|Save As to save a form under a different name or location. However, the .DFM file is not listed separately from the unit file in the Save As dialog box. Even if you make design-time modifications to a form without changing any of the underlying source code, only the .PAS file is displayed when you go to save the file. The two files are inseparable; saving one saves the other.

Saving a form under a different name is a good way to ensure that modifications you make to the form do not affect any other projects that might also be using the form.

To save a form under a different name,

1.	Select the form you want to save.

2.	Choose File|Save As.

3.	In the Save As dialog box, specify a name and a directory for the file.

4.	Choose OK.


See also

Adding a form to the Object Repository

Saving files

Saving projects and individual project files


Masking password characters

See also	Example

You can mask the characters that a user enters into an edit or memo field. Use the PasswordChar property of the Edit, DBEdit, or MaskEdit components to display any characters the user enters as special characters, such as asterisks (*) or pound signs (#).

To see an example, open the Password Dialog Form Template.

Note:	The PasswordChar property of the Edit component in the Password Dialog Form Template, Password, has already been set to *. When the user enters text in this dialog box at run time, only the asterisk character is displayed, so that the user's password is not visible onscreen. (You can enter any character as the PasswordChar property value.)


Example

The following example displays the Password Dialog Form Template when Button1 is clicked.

1.	Start a new, blank project and add the Password Dialog Form Template to it.

2.	Add a button to Form1, and write the following OnClick event handler:

procedure TForm1.Button1Click(Sender: TObject);

begin

  PasswordDlg.ShowModal;

end;

3.	Add Password to Unit1's uses clause, and run the application.

4.	Choose Button1.

The Password dialog box appears.

5.	Type some text into the edit box.

Only asterisks appear.


See also

TEdit component

TDBEdit component

TMaskEdit component

Adding a form to your project


Adding a form to the Object Repository

Once you've designed a custom dialog box, you might want to reuse it in other projects. The best way to do this is to add the form to the Object Repository.

Saving a form as an object is similar to saving a copy of the form under a different name. When you save a form as a object, however, it then appears in the Object Repository. You specify the bitmap and description of the object that appears in this list.

To add your current form to the Object Repository,

1.	Right-click the form to open the Form SpeedMenu.

2.	From the Form SpeedMenu, choose the Add To Repository command.

The The Add To Repository dialog box appears.

3.	In the Title edit box, specify a name for the object.

4.	In the Description edit box, type a brief description of this object.

5.	Choose the Page on which the form should appear in the New Items dialog box.

6.	You can specify an Author of the form, which shows only in the detailed view of the Object Repository.

7.	To specify an icon for the object, choose the Browse button.

The Select Bitmap dialog box appears.

8.	Locate and select the bitmap (if any) you want to use, and choose OK to exit the Select Bitmap dialog box.

9.	Choose OK to accept your specifications, and exit the Add To Repository dialog box.

The next time you choose File|New|New Form, your template appears in the templates list, with the bitmap you chose to represent it, and the description you entered.


Add To Repository dialog box
Add To Repository (Forms)

You use the Add To Repository dialog box to add new forms to the Object Repository.

Add To Repository dialog box

Forms

Displays the names of the forms in the current project.

Title

Use this box to specify the name of the form to be added to the Repository.

Description

Use this box to type a description of the form. The description is dislayed when you select the View Details option from the SpeedMenu in the New Items dilaog box.

Page

Displays a list of the current pages in the Object Repository. Use this option to select the Repository page to which you want to add your form.

Author

Use this box to type the name of the creator of the form you want to add to the Repository.

Browse button

Use the Browse button to change the icon for the form you want to add to the Repistory. The icon represents the bitmap that will appear in the New Items and Repository Options dialog box.  To change the bitmap, click the Browse button to open the Select Bitmap dialog box.

You can use a bitmap of any size, but it will be cropped to 60 x 40 pixels.


Glossary

A

abstract

accelerator key

actual parameter

actual variable

alias

ancestor

application

array

ASCII

B

base type

batch operation

BDE

BDE Adminstrator

BDE Configuration Utility

BLOB

block

Boolean

Borland Database Engine

breakpoints

byte

C

callback routines

call stack

canvas

case variant

char

child

class

class method

client

client area

column

compile

compiler directive

compile time

compile-time error

complete evaluation

component

conditional symbol

const parameter

constant

constant address expression

container application

container component

control

D

data

data access component

data-aware

data control component

data type

database

database server

dataset

DDE client

DDE conversation

DDE server

default ancestor

default event

default new form

default new project

derive

descend

descendant

design time

design-time package

detail table

dispatch

drag

dynamic

dynamic data exchange (DDE)

dynamic-link library (DLL)

E

embedding

encapsulate

end user

enumerated data type

exception

exception handler

execution point

expressions

event

event handler

F

feature

field

file buffer

file type

filter

filter program

focus

form

formal parameter

function

function header

G

global heap

global variable

glyph

grandchild

grandparent

grid

H

handling exceptions

header

heap

heap suballocator

help context

Hint

host type

IJK

IDAPI

identifier

implementation

include file

index

index type

inheritance

instance

integer

integrated debugger

interface

key

L

label

language driver

late binding

linking

literal value

local heap

local symbol information

local variable

lock

logic error

Longint

lookup table

loop

M

main form

master table

method

method identifier

method pointer

MDI application

modal

modeless

module

N

nil

nonvisual component

nonwindowed control

O

object file

object instance variable

object type

OLE

OLE container

OLE object

OLE server

ordinal

override

owner

P

package

parameter

parent

pixel

pointer

power set

primary index

private

private part

procedure

procedure header

program

project

project directory

project file

property

protected

protected block

public

published

Q

qualified identifier

qualified method identifier

qualifier

query

R

raise

real

record

record type

recursion

relational database

report

root class

routine

row

run time

run-time error

run-time library

run-time only

run-time package

S

scalar type

scope

separator

separator bar

set

short-circuit evaluation

Shortint

sizing handles

source code

splash screen

SQL

SQL table

stack

statement

static

step over

string

string list

subrange

switch directive

symbol

T

table

tag field

template

trace into

typecasting

type

type definition

typed constant

U

unit

untyped file

untyped pointer

unqualified identifier

use count

user-defined

V

value parameter

variable parameter

variable

virtual

visual component

W

watches

window handle

windowed control

word

wrapper

XYZ

z-order


abstract

A method that is declared but not implemented. Descendant types must override the abstract method.


accelerator key

Accelerator keys enable the user to access a menu command or component from the keyboard, by pressing Alt+ the appropriate letter, indicated in your code by the preceding ampersand. The letter after the ampersand appears underlined in the menu or component caption.


actual parameter

A variable, expression, or constant that is substituted for a formal parameter in a procedure or function call.


actual variable

A variable that a program can use at run time, as distinguished from the definition of that variable within the program. A location in memory used for storage purposes, as distinguished from an identifier.


alias

A name that specifies the location of database tables. If the database is on a server, an alias also specifies connection parameters for the server.


ancestor

An object from which another object is derived. An ancestor class can be a parent or a grandparent. See default ancestor.


application

An application is the executable file and all related files that a program needs to function which serve a common purpose or purposes, as distinguished from the design and source code of the project. Often used synonymously with 'program'. Compare with program and project.


array

A group of data elements identical in type that are arranged in a single data structure and are randomly accessible through an index.


ASCII

An acronym for "American Standard Code for Information Interchange" and used to describe the byte values assigned to specific characters. Examples: The capital letter A has an ASCII value of 65. The ASCII code for a space is 32.

In Pascal, you can reference a character by its ASCII code prefixed with a number sign (#). Example: To put the symbol for American cents into a character C, for example, you could code "c := #155;".


base type

The type referred to in a pointer declaration, an array declaration, or the enumeration type used in a set declaration. A type declaration builds a new type by combining or referencing one or more other base types, which could themselves be arbitrarily complex.


batch operation

Operations that you perform with the TBatchMove component on groups of records, or on datasets, to add, delete, or copy groups of records in a single operation.


BDE

Borland Database Engine; also referred to in some documentation as IDAPI. Delphi uses this database engine to access and deliver data. BDE maintains information about your PC's environment in the BDE configuration file (usually called IDAPI.CFG). Use the BDE Configuration Utility to change the settings in this configuration file.


BDE Administrator

A program that enables you to change the settings in the BDE configuration file, usually called IDAPI.CFG. The executable file is named BDEADMIN.EXE. Formerly called the BDE Configuration Utility.


BDE Configuration Utility

See BDE Administrator.


BLOB

Binary large object. Many database tables use specific field types to contain BLOB data. Delphi lets you access BLOB data that exists as plain text with the TDBMemo component, and BLOB data that exists as a graphic with the TDBImage component.


block

The associated declaration and statement parts of a program or subprogram. Examples: In the var block of the routine declare an integer variable. Follow the then of your if..then statement with a begin to start a block of code that will be executed only if the condition is met.


Boolean

A data type that can have a value of either True or False. Data size = byte.


Borland Database Engine

See BDE.


breakpoints

A location you mark in your program where you want the program to pause during a debugging session. Once the program's execution has been paused, you can examine the state of your program at that point in its execution. The state of your program includes the values of variables and data structure elements and the routines on the call stack.


byte

A data type capable of holding a value from 0 to 255. A sequence of 8 bits.


callback routines

Routines in your application that are called by Windows and not by your application. For example, EnumFonts is a Windows routine that will call the given callback function for every font installed in the system.


call stack

The list of calls that were made to reach the present location, and which consequently show the path by which the program must return. Available during debugging.


canvas

The graphical drawing surface of an object. The canvas has a brush, a pen, a font, and an array of pixels. The canvas encapsulates the Windows device context.


case variant

1.	The element of a case statement that is examined to determine what code will be executed. In a case statement beginning "Case I of", I is the case variant.

2.	In record type definitions, case variants allow instances of that record to treat the same area of memory as different fields.


char

A Pascal type that represents a single character.


child

1.	A child class is any class that is descended from another. For example, in "type B = class(A)", B is a child of A. Compare with grandchild.

2.	The child of a window appears inside that window and cannot draw outside of its bounds. This is called a child or child window.


class

A list of features representing data and associated code assembled into single entity. A class includes not only features listed in its definition but also features inherited from ancestors. The term "class" is interchangeable with the term "object type."


class method

Class methods provide behavior for a class that is global in nature, or otherwise does not require instance data. A class method is called by using the class name followed by the method (TClass.SomeMethod) and can be called with an instance or without. As such, a class method cannot rely on any properties, fields or instance methods in its executions.


client

Generically, any thing that requests the services of something else. In Object Pascal, a client is any code that uses one or more features of an object or unit. In Windows, a client is code that makes use of the Windows API.

In database systems, a workstation connected to an intelligent "back-end" server from which it can request data. The client workstation can process the data locally and write it back to the server.


client area

In Windows, the area of a control which a program (that is, a client of Windows' services) is allowed to draw on. On a window, for example, that would usually exclude the frame and title bar.


column

The vertical component of a table, sometimes called a field. A column contains one value for each row in a table. See also row.


compile

The act of translating a block of source code into machine instructions. (As opposed to "interpret" which is the line-by-line translation of source code to machine instructions.)

Also see linking.


compiler directive

An instruction to the compiler that is embedded within the program; for example, {$R+} turns on range checking.


compile time

The period of time when the compiler is actively compiling source code.


compile-time error

An error detected by the compiler during compilation, such as a syntax error or unknown identifier.


complete evaluation

Every operand in a Boolean expression built from the and and or operators evaluates, even if the expression result can be determined before the entire expression is evaluated. This is useful when operands are routines that can alter the meaning of a program. Opposite of short-circuit Boolean evaluation.


component

1.	The elements of a Delphi application, iconized on the Component palette. Components, including forms, are objects you can manipulate. Forms are components that can contain other components (forms are not iconized on the Component palette).

2.	In Delphi, any class descended from TComponent is, itself, a component. In the broader sense, a component is any class that can be interacted with directly through the Delphi Form Designer. A component should be self-contained and provide access to its features through properties.

3.	In traditional Pascal, the word "component" is also sometimes used synonymously with feature, as in "The record consists of several components: three string fields and two byte fields."


conditional symbol

Used with conditional compiler directives to specify a condition that is either true or false. You define (set to true) or undefine (set to false) conditional symbols with the $DEFINE and $UNDEF directives.


constant

An identifier with a fixed value in a program. At compile time, all instances of a constant in source code are replaced by the fixed value. Contrast with typed constant.


constant address expression

An expression that takes the address, the offset, or the segment of a global variable, a typed constant, a procedure, or a function.

Constant address expressions cannot reference local variables (stack-based) or dynamic (heap-based) variables, because their addresses cannot be computed at compile time.


const parameter

A const (constant) parameter is one that is passed by reference but that cannot be changed by the procedure. Const is more efficient in performance and memory usage a than a value parameter. See value parameter and variable parameter.


container application

An application that contains an embedded OLE object. (See OLE.)


container component

Any of several component classes that have the inherent ability to contain other components. Examples include TForm, TPanel, TNotebook, and TGroupbox. A container component is the parent of the components it contains.


control

A visual component. Specifically, any descendant of TControl.


data

1.	Information stored in a database. Data may be a single item in a field, a record that consists of a series of fields, or a set of records. Delphi applications can retrieve, add, modify, or delete data in a database.

2.	Generally, any information that has intrinsic value regardless of the means used to access it.


data access component

A Delphi component that enables you to connect to a database and access its data. Data access components are visible on a form only at design time, not at run time.


data-aware

Able to display and update data stored in an underlying table. All Delphi data control components are data-aware.


data control component

A Delphi component that enables you to create the interface of a database application. Many data controls are data-aware versions of component classes available on the Standard page of the Component palette.


data type

A fundamental unit of data definition that defines what kind of data can be stored in memory or in data tables, and what operations can be performed on that data.


database

A collection of data in tables.


database server

A system that manages relational databases. For example, SQL Server is a type of database server.


dataset

A collection of data determined by a TTable, TQuery, or TStoredProc component. A dataset defined by TTable includes every row in a table, and a dataset defined by a TQuery contains a selection of rows and columns from a table.


DDE client

In a DDE conversation, the client is the application that requests data. The DDE client is often called the destination.


DDE conversation

A link between a DDE client application and a DDE server application which provides a means for both applications to continuously and automatically send data back and forth.


DDE server

In a DDE conversation, the server is the application that updates the DDE client. The DDE server is often called the source.


default ancestor

The ancestor of any class that does not specify an ancestor: TObject.


default event

For a given component, the event whose event handler is automatically generated or displayed in the unit source code when you double-click the component at design time. For example, the OnClick event is the default event for a Button component.


default new form

The Form Template that is used to create a new form in the IDE at design time when you choose File | New Form. In a new installation, the Blank Form template is used. You can change the specified Form Template in the Environment Options dialog box (Options | Environment | Gallery).


default new project

The Project Template that is used to open a new Delphi project in the IDE at design time when you choose File | New Project. In a new installation, the Blank Project template is used. You can change the specified Project Template in the Environment Options dialog box (Options | Environment | Gallery).


derive

To create a new class based on an existing class. The new class inherits all of the features of the existing class, which is called its parent or, more generically, an ancestor.


descend

To acquire, in the process of being created, all the characteristics of another class. A class that descends from another is a descendant of the parent class. The process of creating a descendant class is deriving.

See also ancestor, derive, descendant, inheritance, and parent.


descendant

An object derived from another object. A descendant is type compatible with all of its ancestors.


design time

Period when you can use Delphi to design your application, using the form, the Object Inspector, Component palette, Code Editor, and so forth; as opposed to run time, when the application you design is actually running.


design time package

A special dynamic-link library used by the Delphi IDE to install components and to create special property editors for custom components.


detail table

In multi-table relationships, the table whose records are subordinate to those of the master table. In a data model, the detail table is the one being pointed to by another table. For example, in the following data model, all of the tables except CUSTOMER.DB are detail tables.


dispatch

The means of resolving calls to object methods. Dispatching can be either static, virtual, or dynamic.

Do not confuse with TObject.Dispatch which dispatches message procedure calls, not virtuals.


drag

To move an object from one location to another by using your mouse.

To drag an object, click it and continue to hold down the left mouse button while you move the mouse pointer to a new location on your screen. When you are satisfied with the new location, release the mouse button.


dynamic

A form of virtual method which is more space efficient (but less speed efficient) than simple virtual.


dynamic data exchange (DDE)

The process of sending data to and receiving data from other applications through a predefined message protocol. You can use this to exchange data with other applications, or you can control other applications through the use of commands and macros.


dynamic-link library (DLL)

An executable module (extension .DLL) that contains code or resources that can be accessed by other DLLs or applications. In the Windows environment, DLLs permit multiple applications to share code and resources.


embedding

The act of placing one thing within another. In Windows, specifically the capability of one application to provide some or all of the services of another application integrated with its own services. For example, a word processor might allow a spreadsheet to be embedded into a document, allowing the user to write text around the spreadsheet and perhaps even change the spreadsheet while still working in the word processor. See OLE Container.


encapsulate

To provide access to one or more features through an interface that protects clients from relying upon or having to know the inner details of the implementation.


enumerated data type

A user-defined ordinal type that consists of an ordered list of identifiers.


end user

A member of an application's intended audience and, by extension, everyone in that audience. Synonymous with user, but emphasizes the fact that the programmer is not the user.

In Delphi documentation end user refers to a user of an application you develop using Delphi unless otherwise noted.


exception

An event or condition that, if it occurs, breaks the normal flow of execution. Also, an exception is an object that contains information about what error occurred and where it happened.


exception handler

Code designed to resolve the situation in the run-time environment that raised the exception and/or to restore the environment to a stable state afterwards.


execution point

The execution point indicates the next line in your program that will be executed when you run your program through the integrated debugger. The execution point is indicated by highlighted line of code in the Code Editor.


expressions

Part of a statement that represents a value or can be used to calculate a value.


event

A user action, such as a button click, or a system occurrence such as a preset time interval, recognized by a component.

Each component has a list of specific events to which it can respond. Code that is executed when a particular event occurs is called an event handler.


event handler

A form method attached to an event. The event handler executes when that particular event occurs.

When you use the Object Inspector to attach code to a component event, Delphi generates a procedure header and a begin..end block for you. For example, this is the code Delphi generates for a button click event:

procedure TForm1.Button1Click(Sender: TObject);

begin

end;

The code you write inside the begin..end block executes whenever Button1 is clicked.


features

A generic term used to refer the fields of a record, the types, constants, variables and routines of a unit, and the fields, properties, and methods of a class.


field

1.	One possible element of a structured data type (that is, a record or object), a field is an instance of a specific data type. (Compare with property.)

2.	In database terminology, a column of information in a table. A collection of related fields makes up one record. See also record.


file buffer

An area of memory set aside to expedite the transfer of data to and from a file.


file type

A file type refers to the specific data type that a file holds.


filter

Anything used to check or alter data. For example, the file filter in the Save dialog box can be set to show only Pascal files.


filter program

A program that takes output from another program as input and produces an altered, reduced, or verified version of that output.


focus

The component or window that is active in a running application is said to have "focus." Any keyboard input the user enters is directed to that component or window.


formal parameter

An identifier in a procedure or function declaration heading that represents the arguments that will be passed to the subprogram when it is called.

See parameter name for information on a given parameter.


form

To an end user, a form is merely another window. In Delphi, a form is a window that receives components (placed by the programmer at design time, or created dynamically with code at run time), regardless of the intended run-time functionality of the window.


function

A subroutine that computes and returns a value.


function header

Text that gives the name of a routine followed by a list of formal parameters, followed by the function's return type. In a unit, a routine may have a header entered into the interface part, and then again in the implementation part. The second appearance of the header may be an exact duplicate of the header in the interface part, or may be only the name of the routine.


global heap

Memory available to all applications.

Although global memory blocks of any size can be allocated, the global heap is intended only for large memory blocks (256 bytes or more). Each global memory block carries an overhead of at least 20 bytes, and under the Windows standard and 386 enhanced modes, there is a system-wide limit of 8192 global memory blocks, only some of which are available to any given application.

Note:	Delphi suballocates small allocations from large global memory blocks to reduce the liklihood of hitting the system limit. (See HeapLimit, HeapBlock.)


global variable

A variable used by a routine (or the main body of a program) that was not declared by that routine (or a var part of the main body) is considered a global variable by that code. A variable global to one part of a program may be inaccessible to another part of the same program, and hence considered local in that context.


globally unique identifier (GUID)

A GUID is a specific type of universally unique identifier (UUID).  It is a 16 byte (128-bit) binary value that is guaranteed to be unique.


glyph

A bitmap that displays on a BitBtn or SpeedButton component with the component's Glyph property.


grandchild

A class descended from another through one or more intermediate classes. Example: In the following type definition "type E = class(D)", E is the child of D. If D is descended from class C, then E is a grandchild of class C, as well as C's parent, C's parent's parent, and so on, until the root class is reached. C and its ancestors are E's grandparents.


grandparent

A class from which others are descended through one or more intermediate classes. See grandchild.


grid

1.	The evenly spaced dots on the form that aid in placing components during design time (not visible at run time). Control through Options | Environment | Preferences.

2.	In database terminology, an object on a form that enables you to view and edit all the records in a dataset in a spreadsheet-like format. You create a grid with a TDBGrid component.


handling exceptions

Making a specific response to an exception, which then clears the error condition and destroys the exception object.


header

Text that gives the name of a routine followed by a list of formal parameters, followed in the case of a function by the function's return type. In a unit, a routine may have a header entered into the interface part, and then again in the implementation part. The second appearance of the header may be an exact duplicate of the header in the interface part, or may be only the name of the routine.


heap

An area of memory reserved for the dynamic allocation of variables.


heap suballocator

When allocating a memory large block, the heap manager simply allocates a global memory block using the Windows GlobalAlloc routine.

When allocating a small block, the Object Pascal heap manager allocates a larger global memory block and then divides (suballocates) that block into smaller blocks as required. Allocations of small blocks reuse all available suballocation space before the heap manager allocates a new global memory block, which, in turn, is further suballocated.


help context

A number assigned individually to the controls in a program so that when the user activates Help, the Help system can query the focused control and use the help context as a reference to supply information appropriate to what the user is doing.


hint

Pop-up text that appears when the mouse pointer passes over an object in the user interface at run time. Specified in the Hint property of many visual components.


host type

The particular server being used for a process or series of processes, hence "hosting" the activities.


IDAPI

See BDE.


identifier

A programmer-defined name for a specific item (a constant, type, variable, procedure, function, unit, program, or field).


implementation

The second, private part of a unit that defines how the elements in the interface part (the public portion) of the unit work.


include file (.INC)

An include file (.INC) is a source-code file that is included in a compilation using the {$I filename} compiler directive.

Include files are seldom part of a Delphi project, but can optionally be used.


index

1.	A position within a list of elements.

2.	In database terminology, a sort order for a table associated with a specific field or fields, used to locate records quickly. An index performs the following tasks:

Determines the location of records.

Keeps records in sorted order.

Speeds up search operations.


index type

Specifies the type of elements in an array. Valid index types are all the ordinal types except Longint and subranges of Longint.


inheritance

The assumption of the features of one class by another.


instance

A variable of an object type. More generally, a variable of any type. Actual memory is allocated.


integer

A numeric variable type that is a whole number in the range -32,768 to +32,767.


integrated debugger

The integrated debugger is contained within the Integrated Development Environment. This debugger lets you debug your source code without leaving Delphi. The functionality of this debugger can be reached through the Run and View menus.


interface

The first, public part of a unit that describes the constants, types, variables, procedures, and functions that are available within it.


key

A field or group of fields in a table, used to order records. A key has three effects:

The table is prevented from containing duplicate records.

The records are maintained in sorted order based on the key fields.

A primary index is created for the table.


label

An identifier that marks the target for a goto statement.


language driver

Determines a table's sort order and available character set. The BDE Configuration Utility enables you to specify the default language driver for tables.


late binding

When the addess used to call virtual methods or dynamic methods is determined at run time.


linking

The process of turning compiled source code into an executable file. At the linking stage resources are bound into the executable.


literal value

A value that appears in the actual source code, such as the string "Hello, World" or the numeral 1 (as opposed to a calculated value or a declared constant).


local heap

Memory available only to your application or library.

It exists in the upper part of an application's or library's data segment.

The total size of local memory blocks that can be allocated on the local heap is 64K minus the size of the application's stack and static data. For this reason, the local heap is best suited for small memory blocks (256 bytes or less). The default size of the local heap is 8K, but you can change this with the $M compiler directive.


local symbol information

Information used by the IDE to debug a routine. Local symbol information must be enabled in the Project Options dialog box (Options | Project). Enabled by default in new Delphi installations.


local variable

A variable declared within a procedure or function.


lock

A device that prevents other users from viewing, changing, or locking a table while one user is working with it.


logic error

Logic errors occur when your program statements are valid, but the actions they perform are not the actions you intended. For example, logic errors occur when variables contain incorrect values, when graphic images don’t look right, or when the output of your program is incorrect.


Longint (type)

A 4-byte integer, able to store integers in the range -2,147,483,648 to +2,147,483,647.


lookup table

A secondary table that enables database systems to use a small code field to enable many records in a primary table to refer to information stored in the lookup table.

This can be used as a means of ensuring that values entered in a primary table are legitimate values, thus safeguarding data integrity.


loop

A statement or group of statements that repeat until a specific condition is met.


main form

At design time, the first form created in or added to a project. The form designated as the main form can be changed in the Project Options dialog box (Options | Project | Forms). The main form is usually the first displayed at run time, and usually the principal form displayed throughout the execution of the program.


master table

In a multi-table relationship, the primary table of your data model. If you have only one table in your data model, that table is the master table. In a multi-table data model, the master table is the one pointing to other tables. For example, in the following data model, all of the tables except VENDORS.DB are master tables.


method

Procedure or function associated with a particular object.


method identifier

The identifying string or dynamic index of a method.


method pointer

A pointer to a specific method in a specific object.


multiple document interface (MDI) application

An application whose interface consists of a main application window, called the frame window, that can contain multiple child windows, or documents. The child window's document title merges with the parent window's title bar when the child window is maximized.


modal

The run-time state of a form designed as a dialog box in which the user must close the form before continuing with the application. A modal dialog box restricts access to all other areas of the application. See Help for the ShowModal method for more information.


modeless

The run-time state of a form designed as a dialog box in which the user can switch focus away from the dialog box without first closing it. See Help for the Show method for more information.


module

A self-contained routine or group of routines. A unit is an example of a module.


nil

A pointer value referencing nothing. A pointer must be assigned a memory address in order to be meaningfully and safely used.

Note:	Referencing a pointer having a nil value causes a General Protection Fault exception.


nonvisual component

A component that appears at design time as a small picture on the form, but either has no appearance at run time until it is called (like TSaveDialog) or simply has no appearance at all at run time (like TTimer).


nonwindowed control

A nonwindowed control is a control that cannot receive the focus, that cannot be the parent of other controls, and which does not have a window handle.


object files (.OBJ)

An intermediate machine-code file usually produced with an assembler. It is linked with a project or unit using the $L filename compiler directive.

Functions residing in .OBJ files are declared EXTERNAL in Pascal declarations. Object files (.OBJ) are seldom a part of a Delphi project.


object instance variable

The identifier created internally for an instance of an object.


object type

A class.


OLE

Object Linking and Embedding is a method for sharing complex data among applications. With OLE, data from a server application is stored in a container application. The data is stored in an OLE Object.


OLE container

An application that can contain an OLE object. In Delphi, an OLE container application has a TOLEContainer component.


OLE object

The data shared by an OLE server and OLE container. An OLE object can be linked or embedded in the container application. The data for linked objects are stored in an external file; embedded objects are stored in the container application.

Examples of OLE objects are documents, spreadsheets, pictures, and sounds.


OLE server

An application that can create and edit an OLE object.


ordinal (type)

Any Object Pascal type consisting of a closed set of ordered elements.


override

Redefine an object method in a descendant object type.


owner

An object responsible for freeing the resources used by other (owned) objects.


package

A special dynamic-link library used by Delphi applications, the Delphi IDE, or both. Runtime packages provide functionality when a user runs an application. Design-time packages are used to install components in Delphi’s IDE and to create special property editors for custom components.


parameter

A variable or value that is passed to a function or procedure.


parent

1.	The immediate ancestor of a class, as seen in its declaration. Example: In "type B = class(A)", class A is the parent of class B.

2.	Parent property: the component that provides the context within which a component is displayed.


pixel

Any of the individual colored dots that make up an image on the screen. Derived from the words "picture element."


pointer

A variable that contains the address of a specific memory location.


power set

The set of all possible subsets of values of the base type including the empty set.


primary index

An index on the key fields of a table. An index performs the following tasks:

Determines the location of records.

Keeps records in sorted order.

Speeds up search operations.

A primary index typically has a requirement of uniqueness--that is, no duplicate keys can exist.


private

The keyword indicating the beginning of a class declaration.


private part

Elements declared in this part of a class declaration can be used exclusively within the module that contains the class declaration. Outside that module they are unknown and inaccessible.


procedure

A subprogram that can be called from various parts of a larger program. Unlike a function, a procedure returns no value.


procedure declaration

The procedure declaration is the first occurrence of the procedure header that appears in a unit or project.


procedure header

Text that gives the name of a routine followed by a list of formal parameters. In a unit, a routine may have a header declared in the interface part, and then again in the implementation part. The second appearance of the header may be an exact duplicate of the header in the interface part, or may be only the name of the routine.


program

An executable file. Less formally, a program and all the files it needs to run. Contrast with application.


project

The complete catalogue of files and resources used in building an application or DLL. More specifically, the main source code file of the programming effort, which lists the units that the application or DLL depends on.


project directory

The directory in which the project file resides.


project file

The file that contains the source code for a Delphi project. This file has a .DPR extension. It lists all the unit files used by the project and contains the code to launch the application.


property

 A feature that provides controlled access to methods or fields of an object. A published property may also be stored to a file.


protected

Used in class type definitions to make features visible only to the defining class and its descendants.


protected block

The try block of a try...except or try...finally statement.


public

Used in class type definitions to make features visible to clients of that class.


published

Used to make features in class type definitions streamable. Streamable features are visible at design time.


qualified identifier

An identifier that contains a period, that is, includes a qualifier. A qualified identifier forces a particular feature (of an object, record or unit) to be used regardless of other features of the same name that may also be visible within the current scope.


qualified method identifier

An object-type identifier, followed by a period (.), followed by a method identifier. Like any other identifier, you can prefix a qualified method identifier with a unit identifier and a period.


qualifier

An identifier, followed by a period (.), that precedes a method or other identifier to specify a particular symbol reference.


query

A way to retrieve data from your tables. A query can examine the data in a single table or in several tables.


raise

Raising an exception means constructing an exception object to signal an error or other exception condition. The application then must handle the exception.


real

A number represented by floating-point or scientific notation.


record

1.	An instance of a record type.

2.	In database terminology, a horizontal row in a table that contains a group of related fields of data.


record type

A structured data type that consists of one or more fields.


recursion

A programming technique in which a subroutine calls itself. Use care to ensure that a recursion eventually exits. Otherwise, an infinite recursion will cause a stack fault.


relational database

A database management model in which data is stored as rows (records) and columns (fields), and in which the data in one table can access the data in other tables by means of a common data field. The database structure can be used to create one-to-many and many-to-one relationships with data elements.


report

Organized summary or detail information that is presented to the end user either as a printed document or an online display.


root class

A class that itself has no ancestors, and from which all other classes are descended. In Delphi, the root class is TObject.


routine

A procedure or function.


row

The horizontal component of a table, sometimes called a record. A row contains one value for each column in a related group of columns in a table. See also column.


run time

Period when the application you design is running.


run-time error

An error that occurs when the application runs, as opposed to a compile-time error.


run-time library

The standard procedures and functions available to all Object Pascal programs.


run-time only

Routines, properties, events, or components that can be be modified, called, or seen only while your application is running (as opposed to design time).


run-time package

A special dynamic-link library used by Delphi applications to provide functionality when a user runs an application.


scalar type

Any Object Pascal type consisting of ordered components.


scope

The visibility of an identifier to code within a program or unit.


separator

A blank (space) or a comment. Object Pascal treats a comment as a space.


separator bar

A line inserted between menu items. A dash character (-) entered in the Caption property of a new item in the menu designer creates a separator bar at the current position.


set

A collection of zero or more elements of a certain scalar or subrange base type


short-circuit evaluation

Strict left-to-right evaluation of a Boolean expression where evaluation stops as soon as the result of the entire expression is evident. This model guarantees minimum code execution time, and usually minimum code size.


Shortint

A one byte type capable of holding any whole number value from -128 to +127.


sizing handles

The small black rectangles that appear on the perimeter of a component, form or window when selected. You drag them to resize the object.


source code

The line-by-line statements written by the developer of a computer program using an appropriate editing tool and following the syntax rules for a particular programming language.


splash screen

A form you design to "introduce" your application, and which appears immediately at run time while the application main form and secondary forms are being loaded in memory, or while a database server connection is being established. See also main form.


SpeedMenu

A local menu on an object which you can access by right-clicking with a pointing device.


SQL

Structured Query Language, abbreviated SQL and commonly pronounced "sequel." A relational database language used to define, manipulate, search, and retrieve data in databases.


SQL table

A table on a database server defined by SQL.


stack

An area of memory reserved for storing local variables. Also keeps track of program execution and subroutine calls.


statement

The simplest unit in a program; statements are separated by semicolons.


static

Resolved at compile time, as are calls to procedures and methods.


step over

A debugger command that executes a program one line at a time, stepping over procedures while executing them as a single unit. Contrast with trace into.


string

A sequence of characters that can be treated as a single unit of data.


string list

A flexible collection of strings and (potentially) the objects associated with them.


subrange

Any specified contiguous portion of a scalar type.


switch directive

A compiler directive that turns compiler features on or off depending on the state (+ or -) of the switch. For example, {F+} turns the Force Far calls directive on; {F-} turns it off.


symbol

Any identifier. Symbols include reserved words.


table

A structure made up of rows (records) and columns (fields) that contains data.


tag field

A Longint storage for a specific instance of a component to be used as wanted by the programmer.


TDBDataset

A descendant of TDataset that includes the functionality needed to connect to a database, handle passwords, and perform other tasks associated with database connectivity.

You cannot instantiate an object of TDataset directly; you instantiate TTable, TQuery, or another TDataset descendant.


template

A predesigned project or form that serves as a starting point for your application design.


trace into

A debugger command that executes a program one line at a time, tracing into procedures which were compiled with debug information and following the execution of each line. Contrast with step over.


typecasting

The forcing of the compiler to treat an expression of type X as though it were an expression of type Y.

Using AS to typecast object instances causes generation of code to validate the compatibility of the typecast at run time. Normal typecasts are evaluate at compile time and are not validated at run time.


type

A description of how data should be stored and accessed. Contrast with variable--the actual storage of the data.


type compatibility

An instance may be used in place of or assigned to another type it is said to be compatible with.

Integer types are all cross-compatible. A descendant class instance is type-compatible with a variable of an ancestor type. Sibling classes are not type-compatible, nor are ancestors type-compatible with their descendants.


typed constant

A variable that is given a default value upon startup of the application. All global variables occupy a constant space in memory.


type definition

The specification of a non-predefined type. Defines the set of values a variable can have and the operations that can be performed on it.


unit

A independently compileable code module consisting of a public part (the interface part) and a private part (the implementation part).

Every form in Delphi has an associated unit.

The source code of a unit is stored in a .PAS file. A unit is compiled into a binary symbol file with a .DCU extension. The link process combines .DCU files into a single .EXE or .DLL file.


untyped file

Low-level I/O (input/output) channels that let you directly access any disk file regardless of its internal format.


untyped pointer

A pointer that does not point to any specific type. An untyped pointer cannot be referenced without a typecast. (Also see typecast.)


unqualified identifier

An identifier that contains no periods, that is, an identifier with no qualifier. The semantics of an unqualified identifier depend on the current scope. Example: "Create" is an unqualified identifier that will call any routine called "Create" within the current scope (or cause a compile error if no such routine is visible) but "TForm.Create" will call the specific "Create" method which is a feature of TForm. See qualifier.


use count

An internal variable that Windows uses to determine whether or not a DLL should stay in memory. A DLL stays in memory while its use count is greater than zero.

Windows increments Use Count every time an application loads a DLL and decrements whenever an application frees the DLL.


user-defined

Said of a type that is defined by a programmer and not inherently part of the Pascal language. This includes any type definitions you may code or definitions provided by Borland, or any other source.


value parameter

A procedure or function parameter that is passed by value; that is, the value of a parameter is copied to the local memory used by the routine and therefore, changes made to that parameter are local.

See variable parameter, const parameter.


variable parameter

A subroutine parameter that is passed by reference. Changes made to a variable parameter remain in effect after the subroutine has ended. See value parameter, const parameter.


variable

An identifier that represents an address in memory, the contents of which can change at run time.


virtual

Calls are resolved at run time by name.


visual component

A component that is visible, or can be made visible on a form at run time.


watches

A watch expression lets you track the values of program variables or expressions as you step over or trace into your code. Use the Watch List window to view the currently set watches.

As you step through your program, the value of the watch expression will change if your program updates any of the variables contained in the watch expression.


window handle

A number assigned by Windows to a control that must be used to request services for that control from the Windows API.


windowed control

A control that can receive the focus, that can own other controls, and which does have a window handle.


word

In Delphi, A location in memory occupying 2 adjacent bytes; the storage required for a variable of type integer or word. Also, a predefined data type with a range of 0 to 65535.


wrapper

An object, routine, group of objects, or group of routines designed to encapsulate some functionality for the programmer usually for some perceived benefit. VCL is an object-oriented wrapper for the Windows API.


z-order

The conceptual distance of an object from the surface of the screen. Whether or not a control is covered by other controls depends on its z-order relative to those controls.


What's in a mouse event?
What is in a mouse event?

See also

Three mouse events are defined in Delphi:

OnMouseDown

OnMouseMove

OnMouseUp

When a Delphi application detects a mouse action, it calls whatever event handler you have defined for the corresponding event, passing five parameters. Use the information in those parameters to customize your responses to the events. The five parameters are as follows:

Parameter	Meaning

Sender	The object that detected the mouse action

Button	Indicates which button was involved: mbLeft, mbMiddle, or mbRight.

Shift	Indicates the state of the Alt, Ctrl, and Shift keys at the time of the mouse action

X, Y	The coordinates where the event occurred

Most of the time, the most important information in a mouse-event handler is the coordinates, but sometimes you also need to check Button to determine which mouse button caused the event.

Note:	Delphi uses the same criteria as Microsoft Windows in determining which mouse button has been pressed. Thus, if you have switched the default "primary" and "secondary" mouse buttons (so that the right mouse button is now the primary button), clicking the primary (right) button will record mbLeft as the value of the Button parameter.


See also

Responding to the mouse


Adding a field to a form object

See also	Example

When you add a component to a form, Delphi adds a field that represents that component to the form object, and you can refer to the component by the name of its field. You can add your own fields to forms by editing the type declaration at the top of the form's unit.

Adding your own fields provides you with a way to declare variables that are global to all the event handlers associated with the declaring form.

To add a field to an object,

Edit the object's type definition by specifying the field identifier and type after the public directive at the bottom of the declaration.

Delphi puts all fields that represent components and all methods that respond to events before the public directive.


See also

Adding a method to a form object

Modifying the form's type declaration


Example

The following example adds a field called Drawing of type Boolean to the declaration of Form1.

type

  TForm1 = class(TForm)

    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;

      Shift: TShiftState; X, Y: Integer);

    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;

      Shift: TShiftState; X, Y: Integer);

    procedure FormMouseMove(Sender: TObject; Button: TMouseButton;

      Shift: TShiftState; X, Y: Integer);

  public

    Drawing: Boolean;	{ field to track whether button was pressed }

  end;


Adding a speed button to a tool bar

See also

Speed buttons are graphical buttons on tool bars used to represent shortcuts for menu commands and macros.

When you add a speed button to a panel, the panel becomes the container of the speed button, so moving or hiding the panel also moves or hides the speed button.

To add a speed button to a tool bar,

Place the speed button component on the panel that represents the tool bar.

The default height of the tool bar is 41, and the default height of speed buttons is 25. If you set the Top property of each button to 8, they will be vertically centered. The default grid setting will snap the speed button to that vertical position for you.


See also

Assigning a glyph to a speed button

Creating a group of speed buttons

Setting the initial condition of a speed button

Adding speed buttons to a toolbar panel


Assigning a speed button's glyph
Assigning a glyph to a speed button

See also

Each speed button needs a graphical image called a glyph to indicate to the user what the button does. If you supply the speed button only one image, the button manipulates that image to indicate whether the button is pressed, unpressed, selected, or disabled. You can also supply separate, specific images for each state if you prefer.

You normally assign speed-button glyphs at design time, although you can assign different glyphs at run time.

To assign a glyph to a speed button,

1.	Select the speed button.

2.	In the Object Inspector, select the Glyph property.

3.	Double-click the Value column beside Glyph.

Delphi opens the Picture editor.

4.	Click Load in the Picture editor.

Delphi opens a file open dialog box.

5.	Select the .BMP file that contains the image you want on the button, and click OK.

The file open dialog box closes, and the Picture editor displays the selected bitmap image.

6.	Click OK to close the Picture editor.


See also

Adding a speed button to a tool bar

Creating a group of speed buttons

Setting the initial condition of a speed button


Setting the initial condition of a speed button

See also

The appearance of a speed button gives the user information about its state and purpose. Because speed buttons have no caption, it is important that you use the right visual cues to assist users. Part of this involves specifying a glyph for the speed button.

When the user chooses a speed button at run time, by default, the button appears to be pressed down, and then returns to its raised position when the user releases it. You can specify that a speed button remain pressed (in the "down" position) when the user chooses it, by making it into a toggle or by grouping it. A toggle button switches between a pressed and a raised position each time the user chooses it.

To make a non-grouped speed button a toggle,

1.	Set its AllowAllUp property to True.

2.	Set its GroupIndex property to a non-zero value.

You can specify that a speed button toggle appear pressed when it first is displayed in the application, for example to indicate that this is the default selection in a group of buttons.

To make a speed button initially appear in the pressed state,

1.	Make the button into a toggle (see above).

2.	Set its Down property to True.

Just as you can for any component, you can specify that a speed button is not available, initially or at any time while the application is running, by disabling it.

To make a speed button appear disabled,

Set its Enabled property to False.

Note: Setting Enabled to False automatically dims any bitmap displayed on the speed button. If the speed button does not contain a graphic, setting Enabled to False does not provide any visual cue to the user that the button is not available. However, when the user chooses it at run time, the button will not respond.


See also

Adding a speed button to a tool bar

Assigning glyph to a speed button

Creating a group of speed buttons


Creating a group of speed buttons

See also

Creating a group of speed buttons does not require anything other than associating them visually in the form or within a container component. You can, however, create a group of speed buttons whose behavior at run time is interdependent.

When the user chooses a speed button at run time, by default, the button appears to be pressed down, and then returns to its raised position when the user releases it. By grouping speed buttons, you can specify that a speed button in the group remain pressed (in the "down" position) after the user chooses it.

Note:	You can also specify that a non-grouped speed button remain pressed, by making it into a toggle.

If you group speed buttons, you can specify that one speed button in the group must always be pressed at any given time. Alternatively, you can allow all the buttons in the group to be raised.

To associate any number of speed buttons into a group,

Select all the buttons you want in the group, and with the whole group selected, set the GroupIndex property to a nonzero value. (Zero is the default GroupIndex value, and therefore does not create a grouped relationship between speed buttons.)

You can add a speed button to an existing group at any time, by setting its GroupIndex property to the same value as the GroupIndex property of the group. All speed buttons with the same GroupIndex value on the form, or within the same container on the form will interact as a group at run time.

To allow all buttons in a group to appear raised,

Set the AllowAllUp property to True for any speed button in the group.

When you set AllowAllUp to True for a speed button in a group, Delphi automatically sets AllowAllUp to True for all other speed buttons in the same group.


See also

Adding a speed button to a tool bar

Assigning glyph to a speed button

Grouping speed buttons within container components

Setting the initial condition of a speed button


Grouping speed buttons inside container components
Grouping speed buttons within container components

See also

Placing components you wish to group inside a container component (such as a panel,) makes working with them as a group at design time -- for example, moving, copying, deleting -- easier. You can also use properties of the container component to create visual effects such as raised or lowered borders.

Speed buttons do not need to be inside a container to act as a group: Setting the GroupIndex property is what determines which buttons interact together at run time. You can create several groups of speed buttons by setting several different sets of nonzero GroupIndex values. All speed buttons with the same GroupIndex value on the form, or within the same container on the form will interact as a group at run time.


See also

Adding a speed button to a tool bar

Working with toolbars and cool bars

Assigning glyph to a speed button

Creating a group of speed buttons

Setting the initial condition of a speed button


Adding a method to a form object

See also	Example

Any time you find that a number of your event handlers use the same code, you can make your application more efficient by moving the repeated code into a method that all the event handlers can share.

To add a method to a form,

1.	Add the method declaration to the form object.

You can add the declaration in either the public or private parts at the end of the form object's declaration. If the code is just sharing the details of handling some events, it is probably safest to make the shared method private.

2.	Write the method implementation in the implementation part of the form unit.

The header for the method implementation must match the declaration exactly, with the same parameters in the same order.


See also

Adding a field to a form object

Modifying the form's type declaration


Example

The following example eliminates repetitive shape-drawing code from mouse-event handlers by adding a method to the form called DrawShape and calling it from each of the handlers.

1.	Add the declaration of DrawShape to the form object's declaration.

type

  TForm1 = class(TForm)

    ...	{ many fields and methods omitted for brevity }

  public

    { Public declarations }

    procedure DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);

  end;

2.	Write the implementation of DrawShape in the implementation part of the unit.

implementation

{$R *.DFM}

...	{ many other method implementations omitted for brevity }

procedure TForm1.DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);

begin

  with Canvas do

  begin

    Pen.Mode := AMode;

    case DrawingTool of

      dtLine:

        begin

          MoveTo(TopLeft.X, TopLeft.Y);

          LineTo(BottomRight.X, BottomRight.Y);

        end;

      dtRectangle: Rectangle(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y);

      dtEllipse: Ellipse(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y);

      dtRoundRect: RoundRect(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y,

        (TopLeft.X - BottomRight.X) div 2, (TopLeft.Y - BottomRight.Y) div 2);

    end;

  end;

end;

3.	Modify the other event handlers to call DrawShape.

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  DrawShape(Origin, Point(X, Y), pmCopy);	{ draw the final shape }

  Drawing := False;

end;

procedure TForm1.FormMouseMove(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  if Drawing then

  begin

    DrawShape(Origin, MovePt, pmNotXor);	{ erase the previous shape }

    DrawShape(Origin, Point(X, Y), pmNotXor);	{ draw the current shape }

  end;

end;


Adding hidden tool bars

See also

Tool bars do not have to be visible all the time. In fact, it is often convenient to be able to have a number of tool bars available, but show them only when the user wants to use them. Often you create a form that has several tool bars, but hide some or all of them.

To create a hidden tool bar,

1.	Add a tool bar to the form. (Be sure to set Align to alTop.)

2.	Set the panel's Visible property to False.

Although the tool bar remains visible at design time so you can modify it, it remains hidden at run time until the application specifically makes it visible.


See also

Hiding and showing tool bars


Hiding and showing tool bars

See also

Often, you want an application to have multiple tool bars, but you do not want to clutter the form with all of them at once. Or some users might not want tool bars at all. As with all components, tool bars are shown or hidden at run time as specified by your code.

To hide or show a tool bar at run time,

You set its Visible property to False or True, respectively, usually in response to particular user events or changes in the operating mode of the application.

A common practice is to put some kind of close button on each tool bar. When the user clicks that button, the application hides the corresponding tool bar.


See also

Adding hidden tool bars


Placing a status-line panel

See also

In general, status lines appear at the bottom of a form. Aligning the status-line panel to the bottom of the form takes care of both placement and resizing for you.

To add a status-line panel to a form,

1.	Place a panel component on the form.

2.	Set the panel's Align property to alBottom.

3.	Clear the panel's caption.

Once you have added the status-line panel, you can subdivide it into separate status panels. If you are not going to subdivide, you probably want to set the BevelInner and BorderWidth properties to create a 3-D effect. Panels that serve only as containers for smaller panels generally do not change those properties.

You can also align the text within a panel. By default, panels center their captions, but often in a status line, you want to set Align to alLeft.


See also

Status Bars

Subdividing a panel

Creating 3-D panels

Updating the status line


Subdividing a panel

See also

Often you want to divide a panel, particularly one used for a status line, into multiple, independent areas. Although you can achieve a similar effect by carefully formatting the text in a single panel, it is more efficient to use individual panels instead.

To create panels within another panel,

1.	Place a new panel within the panel.

2.	Set any 3-D effects upi want for the new panel.

3.	Set the Align property of the new panel to alLeft.

4.	Move the right side of the new panel to adjust its width.

5.	Clear the new panel's caption.

6.	Repeat steps 1 to 5 as needed for additional panels.

As you add new left-aligned panels to the original panel, they align to each others' right sides. For the last panel, you probably want to set Align to alClient, rather than alLeft, so that the last panel takes up all remaining space in the original panel.


See also

Placing a status-line panel

Creating 3-D panels

Updating the status line


Creating 3-D panels

See also

Panel components used for status-line information usually have a 3-D effect. By default, all panels have an outer bevel to give them a slightly raised appearance. Interior text, however, usually looks better if it is set off by lowering, making the panel look like a frame around the text.

To create the lowered-text effect, you change two properties: BevelInner and BorderWidth.

To create the "engraved" look for the panel, set BevelInner to bvLowered.

To change the space between the inner and outer bevels, change the BorderWidth property. A BorderWidth of 2 gives a good appearance.

The combination of inner and outer bevels creates a frame around the text.


See also

Placing a status-line panel

Subdividing a panel

Updating the status line


Updating the status line

See also	Example

Once you have a status line in a form, you need to update the status-line information. You can set the caption text of a panel at any time to display information you want to provide the user, such as the location of the cursor in a graphics application, or the selected cell in a spreadsheet application.

To update a status-line panel,

Set the panel's Caption property to display the information you want to provide to the user.

Add this code to any event handlers that affect the status a particular panel reflects.


See also

Placing a status-line panel

Subdividing a panel

Creating 3-D panels

OnHint event


Example

The basic technique for updating a working status line involves the following steps:

1.	Add a method identifier to the public section of your main form's declaration:

procedure ShowHint(Sender: TObject);

2.	In the event handler for the main form's OnCreate event, code the following assignment statement:

Application.OnHint := ShowHint;

3.	Write the code for the ShowHint method:

procedure TForm1.ShowHint(Sender: TObject);

begin

 Panel1.Caption := Application.Hint;   {Panel1 is status line}

end;

For more detailed information on creating a working status line, see the example for the OnHint  event.


Drawing on a bitmap

See also

The times when you want to draw directly on a form are relatively rare. More often, an application should draw on a bitmap, since bitmaps are very flexible for operations such as copying, printing, and saving. Delphi's Image component can contain a bitmap, making it easy to put one or more bitmaps into a form.

There are two things you need to do to make the drawing code you write apply to the bitmap instead of the form:

Use the Image component's Canvas instead of the form's canvas.

Attach your event handlers to the appropriate events in the Image component.

Once you move the application's drawing to the bitmap in the Image component, it will be easy to add printing, Clipboard operations, and loading and saving bitmap files.

In addition, the bitmap need not be the same size as the form: It can be either smaller or larger. By adding a scroll-box component to the form and placing the image inside it, you can draw on bitmaps that are much larger than the form or even larger than the screen.

Adding a scrollable bitmap for drawing takes two steps:

Adding a scrollable region

Adding an image component


See also

Loading an image at design time

Providing an area for drawing at run time


Adding a scrollable region

See also

The are many times when an application needs to display more information than will fit in a particular area. Some components, such as list boxes and memos, can automatically scroll their contents. But other components, and sometimes even forms full of components, need to be able to scroll. Delphi provides a way to create such scrolling regions with the ScrollBox component.

A scroll box is much like a panel or a group box, in that it can contain other components. However, a scroll box is normally invisible unless it is needed. If the components contained in the scroll box cannot all fit in the visible area of the scroll box, it automatically displays one or two scroll bars, enabling users to move components outside the visible region into a position where they can be seen and used.

To create a scrolling region,

Place a ScrollBox component on a form and set its boundaries to the region you want to scroll.

You often use the Align property of a scroll box to allow the scroll box to adjust its area to a form or a part of a form. For example, setting Align to alClient causes the scroll box to occupy the entire client area of the form.


See also

Drawing on a bitmap

Adding an image component to the form


Adding an Image component

See also

The Delphi Image component is a kind of place-holder component. It allows you to specify an area on a form that will contain a picture object, such as a bitmap or a metafile. You can either set the size of the image manually, or allow the Image component to adjust to the size of its picture at run time.

You can use an Image component to hold a bitmap that is not necessarily displayed all the time, or which an application needs to use to generate other pictures.

Placing the component

You can place an Image component anywhere on a form. If you set AutoSize to True, then you want to take into consideration the top left corner, as that remains stable even as the component resizes itself. If the Image component is a non-visible holder for a bitmap, you can place it anywhere, just as you would a non-visual component.

Setting the initial picture

If the Image component will always hold a particular picture, you can set its Picture property a4t design time. You can also load the picture into the component from a file at run time. Or, you can use the Image component to provide an area that the user can draw on, such as in a graphics application. To provide a blank bitmap for drawing, you should create it at run time. See Providing an Area for Drawing at Run Time


See also

Adding an Image control

Loading an image


Providing an area for drawing at run time

See also	Example

You can provide an area for the user to draw in at run time by using the Image component to contain a blank bitmap.

To create a blank bitmap when the application starts,

1.	Attach a handler to the OnCreate event for the form that contains the Image component.

2.	Create a bitmap object.

3.	Assign it to the Image component's Picture.Graphic property.

Assigning the bitmap to the picture's Graphic property gives ownership of the bitmap to the picture object. It will therefore destroy the bitmap when it finishes with it, so you should not destroy the bitmap object. You can assign a different bitmap to the picture, at which point the picture disposes of the old bitmap and assumes control of the new one.


See also

Adding an image control

Loading a picture from a file

Replacing a picture


Example

The following code, attached to Form1's OnCreate event, creates a blank bitmap 200 pixels wide by 200 pixels tall, and places the blank bitmap into the image component on the form.

procedure TForm1.FormCreate(Sender: TObject);
var
  Bitmap: TBitmap;	{ temporary variable to hold the bitmap }

begin
  Bitmap := TBitmap.Create;	{ construct the bitmap object }

  Bitmap.Width := 200;	{ assign the initial width... }

  Bitmap.Height := 200;	{ ...and the initial height }

  Image.Picture.Graphic := Bitmap;	{ assign the bitmap to the image component }
end;


Printing graphics

See also	Example

Printing graphic images from a Delphi application is a simple task. The only requirement for printing is that you add the Printers unit to the uses clause of the form that will call the printer. The Printers unit declares a printer object called Printer that has a canvas that represents the printed page.

To print a graphic image,

Copy the image to the printer's canvas.

You can use the printer's canvas just as you would any other canvas. In particular, that means you can copy the contents of a graphic object, such as a bitmap, to the printer directly.


Example

The following code copies the image of a form to the printer in response to a click on a button named PrintButton:

procedure TForm1.PrintButtonClick(Sender: TObject);

begin

  with Printer do

  begin

    BeginDoc;	{ start printing }

    Canvas.Draw(0, 0, Image);	{ draw Image at top left corner of printed page }

    EndDoc;	{ finish printing }

  end;

end;


See also

Using the printer object

Printing the contents of a memo


Working with graphics files

Graphic images that exist only for the duration of one running of an application are of very limited value. Often, you either want to use the same picture every time, or you want to save a created picture for later use. Delphi's Image component makes it easy to load pictures from a file and save them again.

Choose a topic for more information.

Loading a picture from a file

Saving a picture to a file

Replacing a picture


Loading a picture from a file

See also	Example

The ability to load a picture from a file is important if your application needs to modify the picture or if you want to store the picture outside the application so a person or another application can change the picture without changing code.

To load a graphics file into an Image component,

Call the LoadFromFile method of the Image component's Picture object.


See also

Adding an image control


Example

The following code is an OnClick event handler for a menu item called Open. This code retrieves a file name from an open file dialog box and then loads that file into an Image component named Image:

procedure TForm1.Open1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
  begin
    CurrentFile := OpenDialog1.FileName;

    Image.Picture.LoadFromFile(CurrentFile);
  end;
end;


Saving a picture to a file

See also	Example

When you have created or modified a picture, you often want to save the picture in a file for later use. The Delphi Pictureobject can save graphics in several formats, and application developers can create and register their own graphic-file formats so that picture objects can store them as well.

To save the contents of an Image component to a file,

Call the SaveToFile method of the Image component's Picture object.

The SaveToFile method requires the name of a file to save into. If the picture is newly created, it might not have a file name, or a user might want to save an existing picture in a different file. In either case, the application needs to get a file name from the user before saving, as shown in the example code.


See also

Adding an image control

Loading a picture from a file


Example

The following pair of event handlers, written for menu items called Save and Save As, respectively, handles resaving named files, saving unnamed files, and saving existing files under new names.

procedure TForm1.Save1Click(Sender: TObject);
begin
  if CurrentFile <> '' then
    Image.Picture.SaveToFile(CurrentFile)	{ save if already named }

  else SaveAs1Click(Sender);	{ otherwise get a name }
end;

procedure TForm1.Saveas1Click(Sender: TObject);
begin
  if SaveDialog1.Execute then	{ get a file name }
  begin
    CurrentFile := SaveDialog1.FileName;	{ save the user-specified name }

    Save1Click(Sender);	{ then save normally }
  end;
end;


Replacing a picture

See also	Example

You can replace the picture in an Image component at any time. If you assign a new graphic to a picture that already has a graphic, the new graphic replaces the existing one.

To replace the picture in an Image component,

Assign a new graphic to the Image component's Picture property.

Assigning a new bitmap to the picture object's Graphic property causes the picture object to destroy the existing bitmap and take ownership of the new one. Delphi handles the details of freeing the resources associated with the previous bitmap automatically.


See also

Adding an image control

Providing an area for drawing at run time

Loading a picture from a file

Saving a picture to a file


Example

The following code is an OnClick event handler for a menu item called New. This code opens a dialog box, NewBMPForm, that enables the user to choose a size other than the default size used for the existing bitmap area.

procedure TForm1.New1Click(Sender: TObject);
var
  Bitmap: TBitmap;	{ temporary variable for the new bitmap }

begin
  with NewBMPForm do
  begin
    ActiveControl := WidthEdit;	{ make sure focus is on width field }

    WidthEdit.Text := IntToStr(Image.Picture.Graphic.Width);	{ use current dimensions... }

    HeightEdit.Text := IntToStr(Image.Picture.Graphic.Height);	{ ...as default }

    if ShowModal <> idCancel then	{ continue if user does not cancel dialog box }
    begin
      Bitmap := TBitmap.Create;	{ create fresh bitmap object }

      Bitmap.Width := StrToInt(WidthEdit.Text);	{ use specified width }

      Bitmap.Height := StrToInt(HeightEdit.Text);	{ use specified height }

      Image.Picture.Graphic := Bitmap;	{ replace graphic with new bitmap }

      CurrentFile := '';	{ indicate unnamed file }
    end;
  end;
end;


Using the Clipboard with graphics

See also	Example

You can use the Windows Clipboard to copy and paste graphics within your applications or to exchange graphics with other applications. Delphi's Clipboard object makes it easy to handle different kinds of information, including graphics.

Before you can use the Clipboard object in your application, you must add the ClipBrd unit to the uses clause of any unit that needs to access Clipboard data.

Copying graphics to the Clipboard

You can copy any graphical image, including the contents of Image components and graphics on forms, to the Clipboard. Once on the Clipboard, the picture is available to all Windows applications.

To copy a picture to the Clipboard,

Assign the picture to the Clipboard object using the Assign method.

Cutting graphics to the Clipboard

Cutting a graphic to the Clipboard is exactly like copying it, but you also erase the graphic from the source.

To cut a graphic from a picture to the Clipboard,

First copy it to the Clipboard, then erase the original. To erase the original, for example, you might set the area to white.

Pasting graphics from the Clipboard

If the Windows Clipboard contains a graphic, you can paste it into any image object, including Image components and the surface of a form.

To paste a graphic from the Clipboard,

1.	Call the Clipboard's HasFormat method to see whether the Clipboard contains a graphic.

HasFormat is a Boolean function. It returns True if the Clipboard contains an item of the type specified in the parameter. To test for graphics, you pass CF_BITMAP.

2.	Assign the bitmap on the Clipboard to the destination, using the Assign method.


See also

Using the clipboard with text


Example

Example for copying graphics to the Clipboard

Example for cutting graphics to the Clipboard

Example for pasting graphics to the Clipboard


Example

This code copies the picture from an Image component named Image to the Clipboard in response to a click on an Edit|Copy menu item:

procedure TForm1.Copy1Click(Sender: TObject);
begin
  Clipboard.Assign(Image.Picture);
end;


Example

This example removes the selected graphic from the form and copies it onto the Clipboard when the user clicks the Edit|Cut menu item.

procedure TForm1.Cut1Click(Sender: TObject);
var
  ARect: TRect;

begin
  Copy1Click(Sender);	{ copy picture to Clipboard }

  with Image.Canvas do
  begin
    CopyMode := cmWhiteness;	{ copy everything as white }

    ARect := Rect(0, 0, Image.Width, Image.Height);	{ get bitmap rectangle }

    CopyRect(ARect, Image.Canvas, ARect);	{ copy bitmap over itself }

    CopyMode := cmSrcCopy;	{ restore normal mode }
  end;
end;


Example

The following code pastes a picture from the Clipboard into an Image component in response to a click on an Edit|Paste menu item:

procedure TForm1.PasteButtonClick(Sender: TObject);

var

  Bitmap: TBitmap;
begin
  if Clipboard.HasFormat(CF_BITMAP) then	{ check to see if there is a picture }

   begin

     Bitmap := TBitmap.Create; 	{Create a bitmap to hold the contents of the Clipboard}

     try

       Bitmap.Assign(Clipboard);	{get the bitmap off the clipboard using Assign}

       Image.Canvas.Draw(0, 0, Bitmap);	{copy the bitmap to the Image}

     finally

       Bitmap.Free;

     end;

   end;

end;

end;


Drawing graphics at run time

Drawing graphics in a Delphi application is done on the object's canvas rather than directly on the object.

The canvas is a property of the object, and is itself an object with its own properties. The canvas object has four important properties:

A pen for drawing lines

A brush for filling shapes

A font for writing text

An array of pixels to represent the image

Canvases are available only at run time, so you do all your work with canvases by writing code.

To use graphics effectively at run time, you need to understand the following concepts:

Drawing vs. painting

Using the pixel array

Drawing lines and shapes


Drawing vs. painting

When working with graphics, you often encounter the terms drawing and painting. It is important that you understand the difference between the two.

Drawing

Drawing is the creation of a single, specific graphic element, such as a line or a shape, with code. In your code, you tell an object to draw a specific graphic in a specific place on its canvas by calling a drawing method of the canvas.

Painting

Painting is the creation of the entire appearance of an object. At certain times, Windows determines that objects on the screen need to refresh their appearance, so it generates OnPaint events. OnPaint events happen when, for example, a window that was hidden is brought to the front.

Painting usually involves drawing because in response to OnPaint events, an object generally draws some graphics.

An OnPaint event occurs whenever Windows determines that a form needs to be redrawn, such as when another window covering the form goes away. FormPaint is the default name Delphi generates for a handler for a form's OnPaint event.

You can handle OnPaint events when you want to paint a background on a form or when you want to regenerate an image.


Using the pixel array

See also	Example

The Pixels property of a canvas represents the individual colored points that make up the image onscreen.

You rarely need to access Pixels directly, unless you need to find out what color a particular pixel is or to set that pixel's color.

Pixels are the drawing surface of the canvas, and the pen and brush are convenient ways to manipulate groups of pixels.

Manipulating pixels

The pixels of a canvas are a two-dimensional array, with each element in the array having a particular color. You can either read or set individual pixels.

The upper left corner of the canvas is the origin of the coordinate system.

To retrieve the color of a particular pixel,

Read the Pixels property of the canvas, using as indexes the x- and y-coordinates of the pixel you want to read.

To set the color of a particular pixel,

Assign a color value to the Pixels property, using as indexes the x- and y-coordinates of the pixel.


See also

Using pens

Using brushes


Example

Example for reading a pixel

Example for setting a pixel


Example

The following example responds to a click on a check box by changing the color of the text in the check box to the color of the pixel at the point (10, 10) in the form:

procedure TForm1.CheckBox1Click(Sender: TObject);

begin

  CheckBox1.Font.Color := Canvas.Pixels[10, 10];

end;


Example

The following example responds to a click on a button by changing a random pixel on the form to red:

procedure TForm1.Button1Click(Sender: TObject);

begin

  Canvas.Pixels[Random(ClientWidth), Random(ClientHeight)] := clRed;

end;


Using pens

Use the Pen property of a canvas to control how lines appear, including lines drawn as the outlines of shapes. Even though the pen is a property of an object, it itself is also an object.

Draw a straight line by changing a group of pixels that lie between two points.

The tasks below are unique to pens. The following is a list of tasks that either have multiple steps or require some programming knowledge.

Drawing lines and shapes

Moving the pen

Property tasks

The following table summarizes tasks that you can perform simply by setting properties. To find out how to set a property, see Setting component properties.

Task	Property

Change the pen color	Color

Change the pen width	Width

Change the pen style	Style

Change the pen mode	Mode


Moving the pen

The PenPos property stores the current position of the pen. Pen position affects only the drawing of lines; for shapes and text, you specify all the needed coordinates.

To set the pen position,

Call the MoveTo method of the canvas. The upper left corner of the canvas is the coordinate position ( 0, 0 ).

Drawing a line with the LineTo method also moves the current position to the endpoint of the line.


Using brushes

Use the Brushproperty of a canvas to control how you fill areas, including the interior of shapes. Filling an area with a brush changes a large number of adjacent pixels in a specified way.

The tasks below are unique to brushes.

Property tasks

The following table summarizes tasks that you can perform simply by setting properties. If you do not know how to set a property, see Setting component properties.

Property	Task

Color	Changing the fill color

Style	Changing the brush style

Bitmap	Using a bitmap as a brush pattern


Drawing lines and shapes

See also	Example

A canvas has methods for drawing many kinds of shapes.

The canvas draws the outline of the shape with its pen, then fills the interior (when applicable) with its brush.

To draw lines and shapes,

Call the associated method for that graphic.

The following table summarizes the shapes the canvas can draw and the methods needed to draw each shape.

To draw	Method	Comments

Arc	Arc	The Arc method draws an arc along the perimeter of the ellipse bounded by the rectangle.

Chord	Chord	The Chord method draws a line connecting two points on the ellipse bounded by the rectangle.

Ellipse	Ellipse	The Ellipse method draws an ellipse that touches all four sides of the rectangle.

Line	LineTo	LineTo draws a line from the current pen position to the point you specify and makes the endpoint of the line the current position. The canvas draws the line using its pen.

Pie Slice	Pie

Polygon	Polygon	Polygon takes an array of points as its only parameter and connects the points with the pen, then connects the last point to the first to close the polygon. After drawing the lines, Polygon uses the brush to fill the area inside the polygon.

Polyline	PolyLine	The parameter passed to the PolyLine method is an array of points. Drawing a polyline is like performing a MoveTo on the first point and a LineTo on each successive point.

Rectangle	Rectangle	The Rectangle method draws the bounding rectangle.

Rounded Rectangle	RoundRect 	The first four parameters are a bounding rectangle, and the last two indicate how to draw the rounded corners.


See also

Drawing vs. painting

Using pens

Using brushes


Example

The following method draws crossed diagonal lines across a form whenever the form gets painted:

procedure TForm1.FormPaint(Sender: TObject);

begin

  with Canvas do

  begin

    MoveTo(0, 0);

    LineTo(ClientWidth, ClientHeight);

    MoveTo(0, ClientHeight);

    LineTo(ClientWidth, 0);

  end;

end;


Examples

Example for drawing ellipses

Example for drawing polygons

Example for drawing polylines

Example for drawing rectangles

Example for drawing rounded rectangles

Example for drawing straight lines


Example

The following method draws a rhombus in a form:

procedure TForm1.FormPaint(Sender: TObject);

begin

  with Canvas do

    PolyLine([Point(0, 0), Point(50, 0), Point(75, 50), Point(25, 50), Point(0, 0)]);

end;

This example takes advantage of Delphi's ability to automatically create an open-array parameter You can pass any array of points, but an easy way to construct an array quickly is to put its elements in brackets and pass the whole thing as a parameter.


Example

The following method draws a rectangle filling a form's upper left quadrant, then draws an ellipse in the same area:

procedure TForm1.FormPaint(Sender: TObject);

begin

  Canvas.Rectangle(0, 0, ClientWidth div 2, ClientHeight div 2);

  Canvas.Ellipse(0, 0, ClientWidth div 2, ClientHeight div 2);

end;


Example

The following method draws a rounded rectangle in a form's upper left quadrant, rounding the corners as if they were sections of a circle with a diameter of 10 pixels:

procedure TForm1.FormPaint(Sender: TObject);

begin

  Canvas.RoundRect(0, 0, ClientWidth div 2, ClientHeight div 2, 10, 10);

end;


Example

The following example draws a right triangle in the lower left half of a window:

procedure TForm1.FormPaint(Sender: TObject);

begin

  Canvas.Polygon([Point(0, 0), Point(0, ClientHeight),

    Point(ClientWidth, ClientHeight)]);

end;


Add Fields dialog box

Use the Add Fields dialog box to create a persistent field component for a dataset:

To create a persistent field component for a dataset:

1.	Right-click the Fields editor list box.

2.	Choose Add fields from the SpeedMenu. The Add Fields dialog box appears

The Available fields list box displays all fields in the dataset which do not have persistent field components.

3.	Select the fields for which you want to create persistent field components.

4.	Click OK.

Each time you open the dataset, Delphi no longer creates dynamic field components for every column in the underlying database. It only creates persistent components for the fields you specified.

Each time you open the dataset, Delphi verifies that each non-calculated persistent field exists or can be created from data in the database. If it cannot, Delphi raises an exception warning you that the field is not valid, and does not open the dataset.

To delete a persistent field component:

1.	Select the field(s) to remove in the Fields editor list box.

2.	Press the Delete key.

Note:	Fields you remove are no longer available to the dataset and cannot be displayed by data aware controls.


Fields editor

Use the Fields editor at design time to create persistent lists of the field components used by the datasets in your application. Persistent fields component lists are stored in your application, and do not change even if the structure of a database underlying a dataset is changed. All fields in a dataset are either persistent or dynamic.

To start the fields editor:

Double-click the dataset component.

The Fields editor appears.

Using the Fields editor

The Fields editor contains a title bar, navigator buttons, and a list box.

Title bar

The title bar displays the both the name of the data module or form containing the dataset, and the name of the dataset itself.

Navigation buttons

Use these buttons to scroll one-by-one through the records in an active dataset at design time. You can also jump to the first or last record. The buttons are dimmed if the data set is not active or empty.

List box

The List box displays the names of persistent fields components for the dataset. The first time you invoke the Fields editor for a new dataset, the list is empty because the fields components for the dataset are dynamic, not persistent. If you invoke the Fields editor for a dataset that already has persistent fields components, you see the fields component names in the list box.

To add fields to the list of persistent fields for a dataset:

1.	Right-click the list box to invoke the SpeedMenu for  the Fields editor

2.	Choose Add Fields.


Associate attributes dialog box

You can apply attribute sets to fields without having to recreate the settings manually if:

If several fields in datasets used by your application share common formatting properties

and

You have saved those property settings as attribute sets in the Data Dictionary.

If you change the attributes in the Data Dictionary, those changes are automatically applied to every field associated with the set the next time field components are added to the dataset.

To apply an attribute set to a field component:

1.	Double-click the dataset to invoke the Fields editor.

2.	Select the field for which to apply an attribute set.

3.	Right-click the Fields editor list box to invoke the SpeedMenu.

4.	Choose Associate attributes.

5.	Select or enter the attribute set to apply from the Attribute set name dialog box.

If there is an attribute set in the Data Dictionary that has the same name as the current field, that set name appears in the edit box.


Save attributes dialog box

When several fields in the datasets used by your application share common formatting properties, it is more convenient to set the properties for a single field, then store those properties as an attribute set in the Data Dictionary. Attribute sets stored in the data dictionary can be easily applied to other fields.

To create an attribute set based on a field component in a dataset:

1.	Double-click the dataset to invoke the Fields editor.

2.	Select the field for which to set properties.

3.	Set the desired properties for the field in the Object Inspector.

4.	Right-click the Fields editor list box to invoke the SpeedMenu.

5.	Choose Save Attributes to save the current field’s property settings as an attribute set in the Data Dictionary.

The name for the attribute set defaults to the name of the current field. You can specify a different name for the attribute set by choosing Save attributes as instead of Save attributes from the SpeedMenu.

Note:	You can also create attribute sets directly from the Database Explorer. When you create an attribute set from the data dictionary, the set is not applied to any fields, but you can specify two additional attributes in the set: a field type and a data-aware control that is automatically placed on a form when a field based on the attribute set is dragged onto the form. For more information, see the online help for the Database Explorer.


Retrieve attributes

To retrieve an attribute set for a field component that is different in name from the field component name, choose Retrieve attributes to specify the name of the attribute set to retrieve.


Save as attributes

To save an attribute set and assign it a name that differs from the currently selected field component name, choose Save as attributes, and then enter the new attribute set name.


Unassociate attributes

To remove an attribute set assignment for a selected field component, choose Unassociate attributes.


Fields Editor edit options

Use these SpeeeMenu options to edit fields in the Field editor.

Cut

Use this option to remove selected field from the editor and place them on the Windows clipboard.

Copy

Use this option to copy selected fields to the Wincdows clipboard.

Paste

Use this option to paste the clipboard contents into an application.

Delete

Use this option to delete selected fields without copying them to the clipboard.

Select All

Use this option to select all the fields in the fields


DBGrid Columns editor

The Columns editor contains a list box of defined columns, insertion and deletion buttons. Column properties are displayed in the Object Inspector.

At design time, use the DBGrid Columns editor to create a set of personal column objects for the grid. At run time, the State property for a grid with persistent column objects is automatically set to csCustomized.

Column properties determine how data is displayed in the cells of that column.  Most column properties obtain their default values from properties associated with another component, called the default source, such as a grid or an associated field component.

To create persistent columns for a grid control:

1.	Select the DBGrid component in the form.

2.	Right-click to invoke the SpeedMenu, and choose Columns editor.

Columns List box

The Columns list box displays the persistent columns that have been defined for the selected grid. When you first bring up the Columns editor, this list is empty because the grid is in its default state, containing only dynamic columns.

Add button

To create a persistent column for a grid, click on the Add button in the Columns editor.  A new column will entered the list box. Default parameters for the column will be displayed in the Object Repository.

Delete button

Use this button to delete a selected column.

Add All Fields button

To create columns for all the fields in the grid’s dataset, click the Add All Fields button.  If the grid already contained persistent columns, a dialog box asks if you want to delete the existing columns, or append to the column set.

Delete All Columns button

Use this button to delete a persistent column from a grid.

Note:	If you delete all the columns from a grid, the grid reverts to its csDefault state and automatically builds dynamic columns for each field in the dataset.

The Column properties page

In the Columns properties displayed on the Object Inspector. The following table summarizes the properties

Property	Purpose

Alignment	Left justifies, right justifies, or centers the field data in the column. Default source:  TField.Alignment

ButtonStyle	cbsAuto:  (default) Displays a drop-down list if the associated field is a lookup field, or if the column’s PickList property contains data.

cbsEllipsis:  Displays an ellipsis (...) button to the right of the cell.  Clicking on the button fires the grid’s OnEditButtonClick event.

cbsNone:  The column uses only the normal edit control to edit data in the column

Color	Specifies the background color of the cells of the column.  For text foreground color, see the font property. Default Source:  TDBGrid.Color

DropDownRows 	The number of lines of text displayed by the drop-down list. Default: 7

FieldName	Specifies the field name that is associated with this column.  This can be blank.

Font	Invoke the Font editor to define the font attibutes of the column text.

PickList	Invoke the String List editor to define a list of strings to display in a drop-down list in the column. When the user chooses from the list, the corresponding field's value is set to what the user chose. PickList is similar to using a lookup field, but is more useful for lists of items that rarely change or are fixed in the application.

PopupMenu	The PopupMenu property identifies the pop-up menu associated with the control. Assign a value to PopupMenu to make a pop-up menu appear when the user selects the control and clicks the right mouse button. If the TPopupMenu’s AutoPopup property is True, the pop-up menu appears automatically. If the menu’s AutoPopup property is False, display the menu with a call to its Popup method

ReadOnly	True:  The data in the column cannot be edited by the user.

False: (default)  The data in the column can be edited.

Title	Click Title to expand the property list.  Define Allignment, Caption text, Color, Font and Width for the selected column.

Width	Specifies the width of the column in screen pixels. Default Source: derived from TField.DisplayWidth

Restore Defaults

You can discard all property changes in the selected column by right clicking the Restore Defaults button. The column's properties will return to their default values.


Update SQL editor

Use the Update SQL editor to create SQL statements to update a dataset.

The TUpdateSQL object must be associated with a TQuery object by setting the TQuery property UpdateObject to the name of the TUpdateSQL object used to contain the SQL statements.  A datasource, and database name must be selected for the TQuery object . In addition, the SQL property must include a SQL statement defining a table.

To open the SQL editor:

1.	Select the TUpDateSQL object in the form.

2.	Right-click to invoke the SpeedMenu, and choose Update SQL editor.

The Update SQL editor has two pages, the Options page and the SQL page.

The Options page

The Options page is visible when you first invoke the editor.

Table Name

Use the Table Name combo box to select the table to update. The table names are read from the SQL statements entered in the SQL property of the TQuery object. When you specify a table name, the Key Fields and Update Fields list boxes are populated with available columns.

Key Fields

The  is used to specify the columns to use as keys during the update. Generally the columns you specify here should correspond to an existing index, especially for local Paradox and dBASE tables, but having an index is not a requirement.

Update Fields

The Update Fields list box indicates which columns should be updated. When you first specify a table, all columns in the Update Fields list box are selected for inclusion. You can multi-select fields as desired.

Get Table Fields

Read the table fields for the table name entered and list the fields.

Dataset Defaults button

Use this button to restore the default values of the associated dataset. This will cause all fields in the Key Fields list and the Update Fields list to be selected and the table name to be restored.

Select Primary Keys button

Click the Primary Key button to select key fields based on the primary index for a table.

Generate SQL button

After you specify a table, select key columns, and select update columns, click the Generate SQL button to generate the preliminary SQL statements to associate with the update component’s ModifySQL, InsertSQL, and DeleteSQL properties.

The SQL page

Select the SQL page to view and modify the generated SQL statements.  If you have used the Generate SQL button to  generated SQL statements, then when you select this page, the statement for the ModifySQL property is already displayed in the SQL Text memo box. You can edit the statement in the box as needed.

Note:	Keep in mind that generated SQL statements are intended to be starting points for creating update statements.  You may need to modify these statements to make them execute correctly. Test each of the statements directly yourself before accepting them.

Statement Type radio buttons (Modify, Insert, and Delete)

Use these buttons to switch among auto-generated SQL statements to edit them as needed.

To accept the statements and associate them with the update component’s SQL properties, click OK.


New Field dialog
New Field dialog box

See also

Use the New Field dialog box to create new persistent fields as additions to or replacements of the other peristent fields in a dataset. There are three types of persistent fields you can create:

Data fields, which usually replace existing fields (for example to change the data type of a field), are based on columns in the table or query underlying a dataset.

Calculated fields, which displays values calculated at run time by a dataset’s OnCalcFields event handler.

Lookup fields, which retrieve values from a specified dataset at run time based on search criteria you specify.

If the dataset is a client data set, a fourth field type is also available:

InternalCalc fields, which retrieve calculated values that are stored with the dataset (instead of being dynamically calculated in an OnCalcFields event handler).

These types of persistent fields are only for display purposes. The data they contain at run time are not retained either because they already exist elsewhere in your database, or because they are temporary. The physical structure of the table and data underlying the dataset is not changed in any way.

To create a new persistent field component:

1. 	Right click the TQuery object to display the Speed menu, select Fields Editor.

2. 	Right-click the Fields editor list box.

3.	Choose New field from the SpeedMenu.

The New Field dialog box appears.

The New Field dialog box

The New Field dialog box contains three group boxes: Field properties, Field type, and Lookup definition.

Field properties group

The Field properties group box enables you to enter general field component information.

Name

Enter the component’s field name. The name you enter here corresponds to the field component’s FieldName property. Delphi uses this name to build a component name in the Component edit box. The name that appears in the Component edit box corresponds to the field component’s Name property and is only provided for informational purposes (Name contains the identifier by which you refer to the field component in your source code). Delphi discards anything you enter directly in the Component edit box.

Type combo box

The Type combo box in the Field properties group enables you to specify the field component’s data type. You must supply a data type for any new field component you create. For example, to display floating point currency values in a field, select Currency from the drop-down list.

Size

The Size edit box enables you to specify the maximum number of characters that can be displayed or entered in a string-based field or the size of Bytes and VarBytes fields. For all other data types, Size is meaningless.

Field type

Enables you to specify the type of new field component to create. The default type is Data. If you choose Lookup, the Dataset and Source Fields edit boxes in the Lookup definition group box are enabled.

Lookup definition

The Lookup definition group box is only used to create lookup fields. For more information, see Defining a lookup field.


Defining a data field

A data field replaces an existing field in a dataset. For example, for programmatic reasons you might want to replace a TSmallIntField with a TIntegerField. Because you cannot change a field’s data type directly, you must define a new field to replace it.

Note:	Even though you define a new field to replace an existing field, the field you define must derive its data values from an existing column in a table underlying a dataset.

To create a data field in the New Field dialog box:

1.	Enter the name of an existing persistent field in the Name edit box. Do not enter a new field name.

2.	Choose a new data type for the field from the Type combo box. The data type you choose should be different from the data type of the field you are replacing.

3.	Enter the size of the field in the Size edit box, if appropriate. Size is only relevant for fields of type TStringField, TBytesField, and TVarBytesField.

4.	Select Data in the Field type radio group if it is not already selected.

5.	Choose OK. The New Field dialog box closes, the newly defined data field replaces the existing field you specified in Step 1, and the component declaration in the data module or form’s type declaration is updated.

To edit the properties or events associated with the field component, select the component name in the Field editor list box, then edit its properties or events with the Object Inspector.


Defining a calculated field

A calculated field displays values calculated at run time by a dataset’s OnCalcFields event handler. For example, you might create a string field that displays concatenated values from other fields.

To create a calculated field in the New Field dialog box:

1.	Enter a name for the calculated field in the Name edit box. Do not enter the name of an existing field.

2.	Choose a data type for the field from the Type combo box.

3.	Enter the size of the field in the Size edit box, if appropriate. Size is only relevant for fields of type TStringField, TBytesField, and TVarBytesField.

4.	Select Calculated in the Field type radio group.

5.	Choose OK. The newly defined calculated field is automatically added to end of the list of persistent fields in the Field editor list box, and the component declaration is automatically added to the form’s type declaration in the source code.

6.	Place code that calculates values for the field in the OnCalcFields event handler for the dataset.

To edit the properties or events associated with the field component, select the component name in the Field editor list box, then edit its properties or events with the Object Inspector.


Programming a calculated field

After you define a calculated field, you must write code to calculate its value. Otherwise it always has a null value. Code for a calculated field is placed in the OnCalcFields event for its dataset.


Defining a lookup field

A lookup field displays values it searches for at run time based on search criteria. In its simplest form, a lookup field is passed the name of a field to search, a field value to search for, and the field in the lookup dataset whose value it should display.

For example, consider a mail-order application that enables an operator use a lookup field to determine automatically the city and state that correspond to a zip code a customer provides. In that case, the column to search on might be called ZipTable.Zip, the value to search for is the customer’s zip code as entered in Order.CustZip, and the values to return would be those in the ZipTable.City and ZipTable.State columns for the record where ZipTable.Zip matches the current value in the Order.CustZip field.

To create a lookup field in the New Field dialog box:

1.	Enter a name for the lookup field in the Name edit box. Do not enter the name of an existing field.

2.	Choose a data type for the field from the Type combo box.

3.	Enter the size of the field in the Size edit box, if appropriate. Size is only relevant for fields of type TStringField, TBytesField, and TVarBytesField.

4.	Select Lookup in the Field type radio group. Selecting Lookup enables the Dataset and Key Fields combo boxes.

5.	Choose from the Dataset combo box drop-down list the dataset in which to look up field values. The lookup dataset must be different from the dataset for the field component itself, or a circular reference exception is raised at run time. Specifying a lookup dataset enables the Lookup Keys and Result Field combo boxes.

6.	Choose from the Key Fields drop-down list a field in the current dataset for which to match values. To match more than one field, enter field names directly instead of choosing from the drop-down list. Separate multiple field names with semicolons.

7.	Choose from the Lookup Keys drop-down list a field in the lookup dataset to match against the Source Fields field you specified in step 6. To specify more than one field, enter field names directly instead. Separate multiple field names with semicolons.

8.	Choose from the Result Field drop-down list a field in the lookup dataset to return as the value of the lookup field you are creating. To return values from more than one field in the lookup dataset, enter field names directly instead. Separate multiple field names with semicolons.


See also New Fields dialog
See Also

Defining a data field

Defining a calculated field

Programming a calculated field

Defining a lookup field


Query Parameters editor

Invoke the Query Parameters editor at design time to define values for query parameters. The Query Parameters editor lists parameters that have been entered in SQL statements in the SQL property for the TQuery component. Parameters are listed in the correct order. The editor enables you to assign values to them.

To invoke the Query Parameters editor:

1.	Select the query component.

2.	Right-click the component to invoke the SpeedMenu.

3.	Choose Define Parameters.

Using the Query Parameters editor

Parameters defined in the SQL property are displayed. For queries without previously defined parameters, the Parameter name list box is empty.

Data type combo box

The Data type combo box lists the data type for a parameter selected in the list box. If you add a parameter to the SQL property, you must set its data type.

Value edit box

The Value edit box enables you to enter a value for a selected parameter, and the Null Value check box enables you set a Null value for the selected parameter if its data type permits Null values.

Note:	Defining parameters at design time also prepares the query for execution. A query with parameters must be prepared before it can be executed at run time. To signal the end of parameter definition, choose OK.


StoredProc Parameters editor

Many stored procedures require you to pass them a series of input arguments, or parameters, to specify what and how to process. The order in which you specify input parameters is significant, and is determined by the stored procedure definition on the server.

At design time, the easiest and safest way to enter input parameters is to invoke the StoredProc Parameters editor. The StoreProc Parameters editor lists input parameters in the correct order, and lets you assign values to them.

To invoke the StoredProc Parameters editor:

1.	Select the stored procedure component.

2.	Right-click the component to invoke the SpeedMenu.

3.	Choose Define Parameters.

The StoredProc Parameters editor

Parameter name list box

The Parameter name list box displays all input, output, and result parameters for the procedure. Information on input and output parameters is retrieved from the server. For some servers (such as Sybase), parameter information may not be accessible. In this case, the Parameter name list box is empty, and you must add the names and order of input and output parameters in order to use the procedure.

Parameter type combo box

The Parameter type combo box describes whether a parameter selected in the list box is an input, output, or results parameter. If a server’s stored procedure allows it, a single parameter may be both an input and output. If you add a parameter to the list, you must set the parameter type for it.

Data type combo box

The Data type combo box lists the data type for a parameter selected in the list box. If you add a parameter to the list, you must set its data type.

Value edit box

The Value edit box enables you to enter a default value for a selected input parameter. The Null Value check box enables you set a Null value as the default for the selected input parameter if its data type supports Null values.

Add button

The Add button enables you to add parameters to a stored procedure definition when your server does not pass the information to Delphi.

Delete button

Enables you to remove parameters you have added,.

Clear button

Removes all parameters from the list. Do not add, delete, or clear parameters for servers that pass parameter information to Delphi except if you are working with Oracle overloaded stored procedures.

Note:	Defining parameters at design time also prepares the stored procedure for execution. A stored procedure must be prepared before it can be executed at run time. To signal the end of parameter definition, choose OK.


Define Parameter dialog box

See also

The Define Parameter dialog box allows you to create new parameters for a stored procedure when the server does not pass the information to Delphi. Do not add parameters for servers that pass parameter information to Delphi unless you are working with Oracle overloaded stored procedures.

Bring up the Define Parameter dialog box by pressing the Add button in the StoredProc Parameters Editor. After the parameter has been created using the Define Parameter dialog box, you can specify the parameter type (input, output, or results) and give the parameter a default value in the StoredProc Parameters Editor.

Using the Define Parameter dialog box

Parameter Name edit control

Use the Parameter name edit control to provide the name of the parameter.

Data type combo box

Use the Data type combo box to specify the field type for the parameter. The drop-down list provides you with selections for the field types supported by the Delphi field component.  Choose the type expected for the parameter by your server.


Field Link designer

At design time, drop a TDataSource object on the form and define a datasource. Select the TTable component and double-click the MasterFields property in the Object Inspector to invoke the Field Link designer.

The Field Link Designer provides a visual way to link (join) master and detail tables.

Available Indexes combo box

The Available Indexes combo box displays the currently selected index used to join the tables. Unless you specify a different index name in the table’s IndexName property, the default index used for the link is the primary index for the table. Other available indexes defined on the table can be selected from the drop-down list.

To link master and detail tables:

1.	Select the field to use to link the detail table in the Detail Fields list

2.	Select the field to link the master table in the Master Fields list.

3.	Choose Add.

The selected fields are be displayed in the Joined Fields list box. For example,

OrderNo -> OrderNo

Note:	For tables on a database server, the Available Indexes combo box will not appear, and you must manually select the detail and master fields to join in the Detail Fields and Master Fields list boxes.


ListView Columns Editor

Use the ListView Columns editor at design time to control the number of columns and their headings in a listview component.

When the ViewStyle property of a {TListView} component is set to vsReport, and ShowColumnHeaders is set to True, the headings specified with the ListView Columns editor are visible at run time.

To invoke the ListView Columns editor:

Select the TListView object and double-click the Columns property value in the Object Inspector.

Using the ListView Columns editor

The ListView Columns editor contains a Columns list box, Add and Delete buttons, and Move Up/Down buttons to move your cursor.

Column properties are displayed in the Object Inspector when the column is selected.  The column properties are Caption, Alignment, and Width.

Columns list box

The Columns list box displays the names of existing column headings. For new components this list is always empty.

To add a column, click Add. A default entry is made in the list box.  Select the entry and edit column's properties in the Object Inspector.

To delete a column, select the column and click Delete.

Column properties displayed in the Object Inspector.

Caption

Specify the heading for a new or existing column in this box. When you press return,  the name, appears in the Columns list box.

Alignment

Choose the alignment for a column. Choices are Left Justify, Center, and Right Justify. Note that the first column heading is always left justified; you cannot change the alignment for this column.

Width

Specifies the width of the column.


ListView Items Editor

Use the ListView Items editor at design time to add or delete the items displayed in a listview component. You can add or delete new items and sub-items, and you can set the caption, image index, and state index for each item in the ListView Items editor.

To invoke the ListView Items editor:

Select the TListView object and double-click the Items property value in the Object Inspector.

Using the ListView Items editor

The ListView Items editor contains an Items group box with an Items list box, a New Item button, a New SubItem button, and a Delete button. When you first add a listview control to a form, the Items list box is empty and the New SubItem and Delete buttons are disabled.  When you enter or change item properties for a selected item, the Apply button is enabled so that you can activate changes immediately.

The ListView Items editor also contains an Item Properties group box for setting the properties of the listview item currently selected in the Items list box. The Item Properties group box contains a Caption edit box, an Image Index edit box, and a State Index edit box.

Items group box

Create and delete listview items and subitems in the Items group box. To create a new item, click New Item. A default item caption appears in the Items list box. Specify an item's properties, including its caption,  in the Items Properties group box. When you create a new item, or select an existing item, the New SubItem button is enabled so that you can nest items within other items in the listview. If the Items list box contains items, the Delete button is also enabled. To delete an item, select it in the Items list box and click Delete.

Item Properties group box

Set the properties for a selected item in the Item Properties group box. Enter a name for the item in the Caption edit box. As you enter the name, it changes in the Items list box.

To display an image to the left of an item that  is not currently selected, specify the index number of the image in the Image Index edit box. To suppress image display, set Image Index to -1 (the default).

To display an image to the left of an item that is currently selected, specify the index number of the image in the State Index edit box. The index number represents an index into the StateImages property of the listview component. To suppress image display, set State Index to -1 (the default).


TreeView Items Editor

Use the TreeView Items editor at design time to add items to a treeview component, delete items from a treeview component, or load images from disk into a treeview component. You can specify the text associated with individual treeview items, and set the image index, selected index, and state index for items.

To invoke the TreeView Items editor:

Select the TTreeView object and double-click the Items property value in the Object Inspector.

Using the TreeView Items editor

The TreeView Items editor contains an Items group box with an Items list box, a New Item button, a New SubItem button, a Delete button, and a Load button. When you first add a treeview control to a form, the Items list box is empty, and the New SubItem and Delete buttons are disabled. When you enter or change item properties for a selected item the Apply button is enabled so that you can activate changes immediately.

The TreeView Items editor also contains an Item Properties group box for setting the properties of the treeview item currently selected in the Items list box. The Item Properties group box contains a Text edit box, and Image Index edit box, a Selected Index edit box, and a State Index edit box.

Items group box

Create, load,  and delete treeview items and subitems in the Items group box. To load a set of existing treeview items from disk, click Load. To create a new item, click New Item. Default text for the item appears in the Items list box. Specify an item's properties, including its text,  in the Items Properties group box.

When you create a new item, or select an existing item, the New SubItem button is enabled so that you can nest items within other items in the treeview. If the Items list box contains items, the Delete button is also enabled. To delete an item, select it in the Items list box and click Delete.

Item Properties group box

Set the properties for a selected item in the Item Properties group box. Enter text  for the item in the Caption edit box. As you enter the name, it changes in the Items list box.

To display an image to the left of an item that  is not currently selected, specify the index number of the image in the Image Index edit box. To suppress image display, set Image Index to -1 (the default).

To display an image to left of a selected item, specify the index number of the image in the Selected Index edit box. The index is zero-based. To suppress image display, set Selected Index to -1 (the default).

To display an additional image to the left of an item , specify the index number of the image in the State Index edit box. The index number represents an index into the StateImages property of the listview component. The index is zero-based. To suppress image display, set State Index to -1 (the default).


HeaderControl Sections Editor

Use the HeaderControl Sections editor at design time to divide a header control into sections and to assign properties to those sections. A section is a resizeable, moveable header within a header control.

To invoke the HeaderControl Sections editor:

Select the THeaderControl object and double-click the Sections property value in the Object Inspector.

Using the HeaderControl Sections editor

The Sections group box contains a Sections list box that displays the names of individual sections within a header control, a Add button for creating a new section, and a Delete button for deleting sections.

Section properties are displayed in the Object Inspector.

Sections group box

Create and delete sections for a header control in the Sections group box. To create a section, click Add. A numbered section header, named "Untitled," appears in the Sections list box, and the Delete button is enabled. Default properties for Width, Min, Max, Style, Alignment, and Allow click appear in the Object Inspector.

To delete a section, select it in the Sections list box and click Delete.

Section properties

Allignment

Specify the alignment for the text or image displayed in the section at run time. By default, Alignment is set to Left Justify.

AllowClick

To prevent users from clicking a section, uncheck the Allow click check box. Allow click is checked by default, enabling users to click a section.

MaxWidth

Specify the maximum width. The default value for Max is 10000.

MinWIdth

Specify the minimum width. The proposed default value for Min is 0.

Style

Specify the drawing style for the section. By default, Style is set to Text, meaning that text only is displayed in the section. To display an image in the header, set Style to OwnerDraw and write the code to draw an object in the header at run time.

Text

Defines the header title. Enter the title and press return.

Width

Width box, specify a default width (in pixels) for the section. The proposed default value for Width is 50.


StatusBar Panels Editor

Use the StatusBar Panels editor at design time to divide a status bar control into panels and to assign properties to those panels. A panel is a fixed width section within a status bar.

To invoke the StatusBar Panels editor:

Select the TStatusBar object and double-click the Panels property value in the Object Inspector.

Using the StatusBar Panels editor

The Panels editor contains a Panels list box that displays the names of individual panels within a status bar control, an Add button for creating a new panel, and a Delete button for deleting panels.

Panel properties are displayed in the Object Inspector.

Panels group box

Create and delete panels for a status bar control in the Panels group box. To create a panel, click Add. A numbered panel , named "Untitled," appears in the Panels list box, and the Delete button is enabled. Default properties for Width, Style, Bevel, and Alignment appear in the Object Inspector.

To delete a panel, select it in the Panel list box and click Delete.

Panel properties

Set the properties for a selected panel in the Object Inspector..

Alignment

Specify the alignment for the text or image displayed in the panel at run. By default, Alignment is set to Left Justify.

Bevel

Specify the bevel type for the panel. By default, Bevel is set to Lowered.

Style

Specify the drawing style for the panel. By default, Style is set to Text, meaning that text only is displayed in the panel. To display an image in the panel, set Style to OwnerDraw and write the code to draw an object in the panel at run time.

Text

Enter the text to display in the panel within the status bar control.

Width

Specify a default width (in pixels) for the panel. The default value for Width is 50.


Add page dialog box

Specify the name for a new page to add to the Object Repository in the Add page edit box. After you add a page to the Object Repository, it appears as a separate tab sheet when you choose File|New to invoke the New Items dialog box for the Object Inspector.


Rename page dialog box

Specify a new name for a an existing page in the Object Repository in the Rename page edit box. After you rename a page to the Object Repository, it appears in place of the old name on the existing page.


Instantiating forms at run time

Example

If you move a form into the Available Forms List Box in the Forms page of the Project Options dialog box, you must instantiate that form at run time. You do not instantiate any forms that are in the Auto-Create Forms List Box, as Delphi creates these forms automatically at run time.

Instantiate a form a run time when you cannot determine at design time how many instances of the form will be required when the application is running. For example, if you write an MDI application that allows the user to open any number of MDI child windows, you should instantiate each child form at run time. Instantiating forms at run time can also reduce the memory requirements of the application and reduce the amount of startup time when the application is run.

Note:	When creating an application that instantiates forms at run time, make sure that the code of the application does not try to access the instance of the form before it has been created.

To instantiate a form at run time,

1.	Add the name of the unit where the form is declared to the uses clause of the unit that will instantiate the form. This is necessary only if the form type is declared in a different unit.

2.	Declare a memory variable of the type of the form.

3.	Assign the return value of the Create constructor of the form type to the memory variable.

The memory variable specifies the instance of the form type.

Note:	The name of the memory variable identifier that the instance of the form is assigned to should not be the name of an existing object or component. While unique instance names are not required at run time, they are recommended so that the instance of the form is not confused with the Name of another object.


Example

The following example declares a memory variable called SpecForm1 of type TSpecForm.  Then, an instance of TSpecForm is created and assigned to SpecForm1. Finally, SpecForm1 is displayed with the Show method.

Note:	The unit in which TSpecForm is declared (SpecUnit) should be added to the uses clause of the unit that contains this code.

uses ..., SpecUnit;       { Add the unit name to the uses clause }

:

var

  SpecForm1: TSpecForm;   { Declare the form instance variable }

:

SpecForm1 := TSpecForm.Create(Self);  { Create the form instance }

SpecForm1.Show;                        { Show SpecForm1 }


System (brief)

These system keyboard shortcuts apply to the Brief keystroke mapping scheme.

Shortcut	Action or command

F7	Records a keyboard macro

F8	Plays back a keyboard macro

F9	Run|Run

F10	Accesses the menu bar

F11	View|Object Inspector

F12	View|Toggle Form/Unit

Alt+F2	Add watch at cursor

Alt+F7	Run to cursor

Alt+F9	Displays a SpeedMenu

Alt+F10	Project|Compile

Ctrl+F1	Topic search

Ctrl+F2	Run|Program Reset

Ctrl+F3	View|Call Stack

Ctrl+F7	Evaluate/modify

Ctrl+F8	Toggle breakpoint

Ctrl+F9	Project|Compile

Ctrl+F11	Run|Step over

Ctrl+Hyphen	File|Close

Ctrl+F12	View|Units

Shift+ F12	View|Forms

Alt+B	View|Window list

Alt+E	File|Open(Note: opens Open dialog box, even when Code Editor window does not have focus)

Alt+H	Displays context-sensitive Help

Alt+N	Displays the next page

Alt+O	File|Save As(Note: opens Save As dialog box, even when Code Editor window does not have focus)

Alt+-	Displays the previous page

Alt+W	File|Save

Alt+X	File|Exit

Alt+Z	Accesses the File menu


Clipboard control (brief)

These Clipboard keyboard shortcuts apply to the Brief keystroke mapping scheme.

Shortcut	Command

Ins	Edit|Paste

Plus (+)	Edit|Copy

Minus (-)	Edit|Cut


Editor (brief)

See also

These editor keyboard shortcuts apply to the Brief keystroke mapping scheme.

Shortcut	Action or command

F5	Search|Find (forward from cursor position)

F6	Search|Replace (forward from cursor position)

Alt+F2	Control|Maximize

Alt+F5	Search|Find (backward from cursor position)

Alt+F6	Search|Replace (backward from cursor position)

Alt+F9	Displays the local menu

Shift+F4	Tiles windows horizontally

Shift+F5	Search|Search Again

Shift+F6	Repeats the last Search|Replace operation

Esc	Cancels a command at a prompt or enters an escape character at the cursor

Del	Deletes a character or block at the cursor

Star	Edit|Undo

Backspace	Deletes the character to the left of the cursor

Shift+Backspace	Deletes the character to the left of the cursor

Tab	Inserts a tab character

Enter	Inserts a new line with a carriage return

Ctrl+B	Moves to the bottom of the window

Ctrl+C	Centers line in window

Ctrl+D	Moves down one screen

Ctrl+E	Moves up one screen

Ctrl+K	Deletes to the beginning of a line

Ctrl+M	Inserts a new line with a carriage return

Ctrl+S	Performs an incremental search

Ctrl+T	Moves to the top of the window

Ctrl+U	Edit|Redo

Ctrl+Backspace	Deletes the word to the left of the cursor

Ctrl+Enter	Inserts an empty new line

Ctrl+- (dash)	Closes the current page

Alt+A	Marks a non-inclusive block

Alt+B	Displays a list of open files

Alt+C	Mark the beginning of a column block

Alt+D	Deletes a  line

Alt+G	Search|Go to line number

Alt+I	Toggles insert mode

Alt+K	Deletes of the end of a line

Alt+L	Marks a line

Alt+M	Marks an inclusive block

Alt+N	Displays the contents of the next page

Alt+P	Prints the selected block

Alt+Q	Causes next character to be interpreted as an ASCII sequence

Alt+R	Reads a block from a file

Alt+S	Search|Find

Alt+T	Search|Replace

Alt+U	Edit|Undo

Alt+Backspace	Deletes the word to the right of the cursor

Alt+Hyphen	Displays the contents of the previous file buffer

Ctrl+Q+[	Finds the matching delimiter (forward)

Ctrl+Q+Ctrl+[	Finds the matching delimiter (forward)

Ctrl+Q+]	Finds the matching delimiter (backward)

Ctrl+Q+Ctrl+]	Finds the matching delimiter (backward)

Ctrl+O+A  	Open file at cursor

Ctrl+O+B  	Browse symbol at cursor

Ctrl+O+O  	Toggles the case of a selection

Ctrl+F1	Help keyword search

Ctrl+F5	Toggles case-sensitive searching

Ctrl+F6	Toggles regular expression searching


See also

Block commands

Bookmark operations

Cursor movement


Block commands (brief)

See also

These block command keyboard shortcuts apply to the Brief keystroke mapping scheme.

Shortcut	Action

Alt+A	Marks a non-inclusive block

Alt+C	Marks a column as a block

Alt+L	Marks a line as a block

Alt+M	Marks an inclusive block

Alt+P	Prints the contents of a block

Alt+R	Reads a block from a file


See also

Bookmark operations

Cursor movement

Editor


Bookmark operations (brief)

See also

These bookmark operations keyboard shortcuts apply to the Brief keystroke mapping scheme.

Shortcut	Action

Alt+0	Sets bookmark 0

Alt+1	Sets bookmark 1

Alt+2	Sets bookmark 2

Alt+3	Sets bookmark 3

Alt+4	Sets bookmark 4

Alt+5	Sets bookmark 5

Alt+6	Sets bookmark 6

Alt+7	Sets bookmark 7

Alt+8	Sets bookmark 8

Alt+9	Sets bookmark 9

Alt+J+0	Goes to bookmark 0

Alt+J+1	Goes to bookmark 1

Alt+J+2	Goes to bookmark 2

Alt+J+3	Goes to bookmark 3

Alt+J+4	Goes to bookmark 4

Alt+J+5	Goes to bookmark 5

Alt+J+6	Goes to bookmark 6

Alt+J+7	Goes to bookmark 7

Alt+J+8	Goes to bookmark 8

Alt+J+9	Goes to bookmark 9


See also

Block commands

Cursor movement

Editor


Cursor movement (brief)

See also

These cursor movement keyboard shortcuts apply to the Brief keystroke mapping scheme.

Shortcut	Action

UpArrow	Moves up one line in the same column position

DownArrow	Moves down one line in the same column position

Home	Moves to the start of a line

End	Moves to the end of a line

Left Arrow	Moves one character to the left

Right Arrow	Moves one character to the right

PgDn	Moves down one screen in the current window

PgUp	Moves up one screen in the current window

Ctrl+Left Arrow	Moves one word to the left

Ctrl+Right Arrow	Moves one word to the right

Ctrl+PgDn	Moves to the end of a file

Ctrl+PgUp	Moves to the beginning of a file

Shift+Tab	Moves backward one tab stop

Shift+Home	Moves to the first column in a window

Shift+End	Moves to the last column in a window

Ctrl+Home	Moves to the top of a screen in the same column position

Ctrl+End	Moves to the bottom of a screen in the same column position

Ctrl+B	Moves to the bottom of the window

Ctrl+C	Moves to the center of the window

Ctrl+D	Scrolls down one screen

Ctrl+E	Scrolls down one screen


See also

Block commands

Bookmark operations

Editor


System (classic)

These system keyboard shortcuts apply to the Classic keystroke mapping scheme.

Shortcut	Action or command

F1	Displays context-sensitive Help

F2	File|Save

F3	File|Open

F4	Run to Cursor

F5	Control|Maximize

F6	Displays the next page

F7	Run|Trace Into

F8	Run|Step Over

F9	Run|Run

F11	View|Object Inspector

F12	View|Toggle Form/Unit

Alt+F3	File|Close

Alt+F10	Displays a SpeedMenu

Alt+X 	File|Exit

Alt+0 	View|Window LIst

Ctrl+F1	Topic Search

Ctrl+F2	Run|Program Reset

Ctrl+F3	View|Call Stack

Ctrl+F4	Evaluate/Modify

Ctrl+F7	Add Watch at Cursor

Ctrl+F8	Toggle Breakpoint

Ctrl+F9	Project|Compile

Ctrl+F12	View|Units

Shift+F12	View|Forms

Ctrl+Shift+P	Plays back a keyboard macro

Ctrl+Shift+R	Records a keyboard macro

Ctrl+Shift+S	Performs an incremental search

Ctrl+K+D	Accesses the menu bar

Ctrl+K+S	File|Save


Clipboard control (classic)

These Clipboard control keyboard shortcuts apply to the Classic keystroke mapping scheme.

Shortcut	Command

Ctrl+Ins	Edit|Copy

Shift+Del	Edit|Cut

Shift+Ins	Edit|Paste


Editor (classic)

See also

These editor keyboard shortcuts apply to the Classic keystroke mapping scheme.

Shortcut	Action or command

F1	Topic Search

Ctrl+F1	Topic Search

F6	Displays the next page

Shift+F6	Displays the previous page

Ctrl+A	Moves one word left

Ctrl+C	Scrolls down one screen

Ctrl+D	Moves the cursor right one column, accounting for the autoindent setting

Ctrl+E	Moves the cursor up one line

Ctrl+F	Moves one word right

Ctrl+G	Deletes the character to the right of the cursor

Ctrl+H	Deletes the character to the left of the cursor

Ctrl+I	Inserts a tab

Ctrl+L	Search|Search Again

Ctrl+N	Inserts a new line

Ctrl+P	Causes next character to be interpreted as an ASCII sequence

Ctrl+R	Moves up one screen

Ctrl+S	Moves the cursor left one column, accounting for the autoindent setting

Ctrl+T	Deletes a word

Ctrl+V	Turns insert mode on/off

Ctrl+W	Moves down one screen

Ctrl+X	Moves the cursor down one line

Ctrl+Y	Deletes a line

Ctrl+Z	Moves the cursor up one line

Ctrl+Shift+S	Performs an incremental search

End 	Moves to the end of a line

Home	Moves to the start of a line

Enter	Inserts a carriage return

Ins 	Turns insert mode on/off

Del 	Deletes the character to the right of the cursor

Backspace	Deletes the character to the left of the cursor

Tab 	Inserts a tab

Space	Inserts a blank space

Left Arrow	Moves the cursor left one column, accounting for the autoindent setting

Right Arrow	Moves the cursor right one column, accounting for the autoindent setting

Up Arrow	Moves up one line

Down Arrow	Moves down one line

Page Up	Moves up one page

Page Down	Moves down one page

Ctrl+Left Arrow	Moves one word left

Ctrl+Right Arrow	Moves one word right

Ctrl+Home	Moves to the top of a screen

Ctrl+End 	Moves to the end of a screen

Ctrl+PgDn	Moves to the bottom of a file

Ctrl+PgUp	Moves to the top of a file

Ctrl+Backspace	Move one word to the right

Ctrl+Del 	Deletes a currently selected block

Ctrl+Space	Inserts a blank space

Ctrl+Enter	Opens file at cursor

Ctrl+Tab	Moves to the next page

Shift+Tab 	Deletes the character to the left of the cursor

Shift+Backspace	Deletes the character to the left of the cursor

Shift+Left Arrow	Selects the character to the left of the cursor

Shift+Right Arrow	Selects the character to the right of the cursor

Shift+Up Arrow	Moves the cursor up one line and selects from the left of the starting cursor position

Shift+Down Arrow	Moves the cursor down one line and selects from the right of the starting cursor position

Shift+PgUp	Moves the cursor up one screen and selects from the left of the starting cursor position

Shift+PgDn	Moves the cursor down one line and selects from the right of the starting cursor position

Shift+End 	Selects from the cursor position to the end of the current line

Shift+Home	Selects from the cursor position to the start of the current line

Shift+Space	Inserts a blank space

Shift+Enter	Inserts a new line with a carriage return

Shift+Ctrl+Tab	Moves to the previous page

Ctrl+Shift+Left Arrow	Selects the word to the left of the cursor

Ctrl+Shift+Right Arrow	Selects the word to the right of the cursor

Ctrl+Shift+Home	Selects from the cursor position to the start of the current file

Ctrl+Shift+End 	Selects from the cursor position to the end of the current file

Ctrl+Shift+PgDn	Selects from the cursor position to the bottom of the screen

Ctrl+Shift+PgUp	Selects from the cursor position to the top of the screen

Ctrl+Shift+Tab	Moves to the previous page

Alt+Backspace	Edit|Undo

Alt+Shift+Backspace	Edit|Redo

Alt+Shift+Left Arrow	Selects the column to the left of the cursor

Alt+Shift+Right Arrow	Selects the column to the right of the cursor

Alt+Shift+Up Arrow	Moves the cursor up one line and selects the column from the left of the starting cursor position

Alt+Shift+Down Arrow	Moves the cursor down one line and selects the column from the left of the starting cursor position

Alt+Shift+Page Up	Moves the cursor up one screen and selects the column from the left of the starting cursor position

Alt+Shift+Page Down	Moves the cursor down one line and selects the column from the right of the starting cursor position

Alt+Shift+End	Selects the column from the cursor position to the end of the current line

Alt+Shift+Home	Selects the column from the cursor position to the start of the current line

Ctrl+Alt+Shift+Left Arrow	Selects the column to the left of the cursor

Ctrl+Alt+Shift+Rght Arrow	Selects the column to the right of the cursor

Ctrl+Alt+Shift+Home	Selects the column from the cursor position to the start of the current file

Ctrl+Alt+Shift+End	Selects the column from the cursor position to the end of the current file

Ctrl+Alt+Shift+Page Up	Selects the column from the cursor position to the bottom of the screen

Ctrl+Alt+Shift+Page Down	Selects the column from the cursor position to the top of the screen


See also

Block commands

Bookmark operations

Cursor movement

Miscellaneous commands


System (default)

These system keyboard shortcuts apply to the Default keystroke mapping scheme.

Shortcut	Action or command

F1	Displays context-sensitive Help

F4	Run|Go to Cursor

F5	Run|Toggle Breakpoint

F7	Run|Trace Into

F8	Run|Step Over

F9	Run|Run

F11	View|Object Inspector

F12	View|Toggle Form/Unit

Alt+F10	Displays a SpeedMenu

Alt+0	View|Window List

Ctrl+F1	Help|Topic Search

Ctrl+F2	Run|Program Reset

Ctrl+F3	View|Call Stack

Ctrl+F4	Closes current file

Ctrl+F5	Add Watch at Cursor

Ctrl+F7	Evaluate/Modify

Ctrl+F9	Project|Compile

Ctrl+F12	View|Units

Shift+F12	View|Forms

Ctrl+Shift+P	Plays back a key macro

Ctrl+Shift+R	Records a key macro

Ctrl+K+D	Accesses the menu bar

Ctrl+K+S	File|Save


Clipboard control (default)

These Clipboard keyboard shortcuts apply to the Default keystroke mapping scheme.

Shortcut	Command

Ctrl+Ins	Edit|Copy

Shift+Del	Edit|Cut

Shift+Ins	Edit|Paste

Ctrl+C	Edit|Copy

Ctrl+V	Edit|Paste

Ctrl+X	Edit|Cut


Editor (default)

See also

These editor keyboard shortcuts apply to the Default keystroke mapping scheme.

Shortcut	Action or command

F1	Help|Topic Search

Ctrl+F1	Help|Topic Search

F3	Search|Search Again

Ctrl+E	Search|Incremental Search

Ctrl+F	Search|Find

Ctrl+I	Inserts a tab character

Ctrl+j	Templates pop-up menu

Ctrl+N	Inserts a new line

Ctrl+P	Causes next character to be interpreted as an ASCII sequence

Ctrl+R 	Search|Replace

Ctrl+S	File|Save

Ctrl+T	Deletes a word

Ctrl+Y	Deletes a line

Ctrl+Z	Edit|Undo

Ctrl+<space bar>	Code Completion pop-up window

Ctrl+Shift+g	Inserts a new Globally Unique Identifier (GUID)

Ctrl+Shift+I	Indents block

Ctrl+Shift+U	Outdents block

Ctrl+Shift+Y	Deletes to the end of a line

Ctrl+Shift+Z	Edit|Redo

Ctrl+Shift+<space bar>	Code Parameters pop-up window

Alt+[	Finds the matching delimiter (forward)

Alt+]	Finds the matching delimiter (backward)

End 	Moves to the end of a line

Home	Moves to the start of a line

Enter	Inserts a carriage return

Ins 	Turns insert mode on/off

Del 	Deletes the character to the right of the cursor

Backspace	Deletes the character to the left of the cursor

Tab 	Inserts a tab

Space	Inserts a blank space

Left Arrow	Moves the cursor left one column, accounting for the autoindent setting

Right Arrow	Moves the cursor right one column, accounting for the autoindent setting

Up Arrow	Moves up one line

Down Arrow	Moves down one line

Page Up	Moves up one page

Page Down	Moves down one page

Ctrl+Left Arrow	Moves one word left

Ctrl+Right Arrow	Moves one word right

Ctrl+Tab 	Moves to the next page

Ctrl+Shift+Tab	Moves to the previous page

Ctrl+Backspace	Deletes the word to the right of the cursor

Ctrl+Home	Moves to the top of a file

Ctrl+End 	Moves to the end of a file

Ctrl+Del 	Deletes a currently selected block

Ctrl+Space	Inserts a blank space

Ctrl+PgDn	Moves to the bottom of a screen

Ctrl+PgUp	Moves to the top of a screen

Ctrl+Up Arrow	Scrolls up one line

Ctrl+Down Arrow	Scrolls down one line

Ctrl+Enter	Opens file at cursor

Shift+Tab 	Moves the cursor to the left one tab position

Shift+Backspace	Deletes the character to the left of the cursor

Shift+Left Arrow	Selects the character to the left of the cursor

Shift+Right Arrow	Selects the character to the right of the cursor

Shift+Up Arrow	Moves the cursor up one line and selects from the left of the starting cursor position

Shift+Down Arrow	Moves the cursor down one line and selects from the right of the starting cursor position

Shift+PgUp	Moves the cursor up one screen and selects from the left of the starting cursor position

Shift+PgDn	Moves the cursor down one line and selects from the right of the starting cursor position

Shift+End 	Selects from the cursor position to the end of the current line

Shift+Home	Selects from the cursor position to the start of the current line

Shift+Space	Inserts a blank space

Shift+Enter	Inserts a new line with a carriage return

Ctrl+Shift+Left Arrow	Selects the word to the left of the cursor

Ctrl+Shift+Right Arrow	Selects the word to the right of the cursor

Ctrl+Shift+Home	Selects from the cursor position to the start of the current file

Ctrl+Shift+End 	Selects from the cursor position to the end of the current file

Ctrl+Shift+PgDn	Selects from the cursor position to the bottom of the screen

Ctrl+Shift+PgUp	Selects from the cursor position to the top of the screen

Ctrl+Shift+Tab	Moves to the previous page

Alt+Backspace	Edit|Undo

Alt+Shift+Backspace	Edit|Redo

Alt+Shift+Left Arrow	Selects the column to the left of the cursor

Alt+Shift+Right Arrow	Selects the column to the right of the cursor

Alt+Shift+Up Arrow	Moves the cursor up one line and selects the column from the left of the starting cursor position

Alt+Shift+Down Arrow	Moves the cursor down one line and selects the column from the left of the starting cursor position

Alt+Shift+Page Up	Moves the cursor up one screen and selects the column from the left of the starting cursor position

Alt+Shift+Page Down	Moves the cursor down one line and selects the column from the right of the starting cursor position

Alt+Shift+End	Selects the column from the cursor position to the end of the current line

Alt+Shift+Home	Selects the column from the cursor position to the start of the current line

Ctrl+Alt+Shift+Left Arrow	Selects the column to the left of the cursor

Ctrl+Alt+Shift+Rght Arrow	Selects the column to the right of the cursor

Ctrl+Alt+Shift+Home	Selects the column from the cursor position to the start of the current file

Ctrl+Alt+Shift+End	Selects the column from the cursor position to the end of the current file

Ctrl+Alt+Shift+Page Up	Selects the column from the cursor position to the bottom of the screen

Ctrl+Alt+Shift+Page Down	Selects the column from the cursor position to the top of the screen


See also

Block commands

Bookmark operations

Cursor movement

Miscellaneous commands


System (Epsilon)

These system keyboard shortcuts apply to the Epsilon keystroke mapping scheme.

Shortcut	Action or command

F1	Displays context-sensitive Help

F5	Toggle Breakpoint

F8	Run|Step Over

F9	Run|Run

F10	Edit|Redo

F11	View|Object Inspector

F12	View|Toggle Form/Unit

Alt+F9	Project|Compile

Alt+F10	Displays a SpeedMenu

Alt+0	View|Window List

Ctrl+F2	Run|Program Reset

Ctrl+F5	Run|Add Watch

Ctrl+F6	Displays the next page

Ctrl+Shift+F6	Displays the previous page

Ctrl+F7	File|Save As

Ctrl+F9	Project|Compile

Ctrl+F12	View|Units

Shift+F12	View|Forms

Ctrl+X+(	Records a keyboard macro

Ctrl+X+)	Ends a keyboard macro recording

Ctrl+X+e	Plays back the the last keyboard macro recorded

Ctrl+X+E	Plays back the the last keyboard macro recorded

Ctrl+X+b	Displays a list of open files

Ctrl+X+B	Displays a list of open files

Ctrl+X+s	File|Save As

Ctrl+X+S	File|Save As

Ctrl+X+Ctrl+F	File|Open

Ctrl+X+Ctrl+S	File|Save

Ctrl+X+Ctrl+W	File|Save


Clipboard control (Epsilon)

These Clipboard keyboard shortcuts apply to the Epsilon keystroke mapping scheme.

Shortcut	Action or command

Ctrl+Y	Edit|Paste

Alt+w	Edit|Copy

Esc+@w	Edit|Copy

Ctrl+Alt+w	Edit|Copy (appends to current contents)

Esc+Ctrl+w	Edit|Copy (appends to current contents)


Editor (Epsilon)

See also

These editor keyboard shortcuts apply to the Epsilon keystroke mapping scheme.

Shortcut	Action or command

Ctrl+H	Deletes the character to the left of the current cursor position

Backspace	Deletes the character to the left of the current cursor position

Alt+Del	Deletes all text in the block between the cursor and the previous matching delimiter (cursor must be on ')', '}' or ']')

Esc+Del	Deletes all text in the block between the cursor and the previous matching delimiter (cursor must be on ')', '}' or ']')

Ctrl+Alt+H	Deletes the word to the left of the current cursor position

Alt+Backspace	Deletes the word to the left of the current cursor position

Esc+BackSpace	Deletes the word to the left of the current cursor position

Esc+Ctrl+H	Deletes the word to the left of the current cursor position

Ctrl+D	Deletes the currently selected character or character to the right of the cursor

Del	Deletes the currently selected character or character to the right of the cursor

Alt+\	Deletes spaces and tabs around the cursor on the same line

Esc+\	Deletes spaces and tabs around the cursor on the same line

Ctrl+Alt+K	Deletes all text in the block between the cursor and the next matching delimiter (cursor must be on ')', '}' or ']')

Esc+Ctrl+K	Deletes all text in the block between the cursor and the next matching delimiter (cursor must be on ')', '}' or ']')

Ctrl+X+0	Deletes the contents of the current window

Alt+d	Deletes to word to the right of the cursor

Esc+@d	Deletes to word to the right of the cursor

Ctrl+K	Cuts the contents of line and places it in the Clipboard

Ctrl+Alt+B	Locates the next matching delimiter (cursor must be on ')', '}' or ']')

Esc+Ctrl+B	Locates the next matching delimiter (cursor must be on ')', '}' or ']')

Alt+)	Locates the next matching delimiter (cursor must be on ')', '}' or ']')

Esc+)	Locates the next matching delimiter (cursor must be on ')', '}' or ']')

Alt+Shift+O	Locates the next matching delimiter (cursor must be on ')', '}' or ']')

Ctrl+Alt+F	Locates the previous matching delimiter (cursor must be on ')', '}' or ']')

Esc+Ctrl+F	Locates the previous matching delimiter (cursor must be on ')', '}' or ']')

Alt+c	Capitalizes the current word

Esc+@c	Capitalizes the current word

Ctrl+L	Centers the active window

Ctrl+M	Inserts a carriage return

Ctrl+X+i	Inserts the contents of a file at the cursor

Ctrl+X+I	Inserts the contents of a file at the cursor

Ctrl+O	Inserts a new line after the cursor

Alt+x	Invokes the specified command or macro

Esc+@x	Invokes the specified command or macro

F2	Invokes the specified command or macro

Ctrl+X+Ctrl+X	Exchanges the locations of the cursor position and a bookmark

Ctrl+Shift+-	Displays context-sensitive Help

Alt+Shift+/	Displays context-sensitive Help

Alt+?	Displays context-sensitive Help

Esc+?	Displays context-sensitive Help

Ctrl+_	Displays context-sensitive Help

Ctrl+X+,	Browses the symbol at the cursor

Tab	Inserts a tab

Alt+Tab	Indents to the current line to the text on the previous line

Esc+Tab	Indents to the current line to the text on the previous line

Alt+l	Converts the current word to lowercase

Esc+@l	Converts the current word to lowercase

Ctrl+X+m	Project|Compile

Ctrl+X+M	Project|Compile

Esc+End	Displays the next window in the buffer list

Ctrl+X+n	Displays the next window in the buffer list

Ctrl+X+N	Displays the next window in the buffer list

Esc+Home	Displays the previous window in the buffer list

Ctrl+X+p	Displays the previous window in the buffer list

Ctrl+X+P	Displays the previous window in the buffer list

Ctrl+X+Ctrl+E	Invoke a command processor

Ctrl+Q	Interpret next character as an ASCII code

Ctrl+X+r	Edit|Redo

Ctrl+X+R	Edit|Redo

F10	Edit|Redo

Ctrl+F10	Edit|Redo

Ctrl+X+Ctrl+R	Edit|Redo

Ctrl+X+u	Edit|Undo

Ctrl+X+U	Edit|Undo

F9	Edit|Undo

Ctrl+F9	Edit|Undo

Ctrl+X+Ctrl+U	Edit|Undo

Ctrl+S	Incrementally searches for a string entered from the keyboard

Ctrl+R	Incrementally searches backward through the current file

Ctrl+Alt+S	Search|Find (using regular expressions)

Esc+Ctrl+S	Search|Find (using regular expressions)

Ctrl+Alt+R	Search|Find (using regular expressions; backward from cursor)

Esc+Ctrl+R	Search|Find (using regular expressions; backward from cursor)

Alt+Shift+5	Search|Replace

Alt+Shift+7	Search|Replace

Alt+&	Search|Replace

Esc+&	Search|Replace

Alt+%	Search|Replace

Esc+%	Search|Replace

Alt+*	Search|Replace (using regular expressions)

Esc+*	Search|Replace (using regular expressions)

Ctrl+X+Ctrl+N	Search|Find Error

Ctrl+X+g	Search|Go To Line Number

Ctrl+X+G	Search|Go To Line Number

Crl+T	Transposes the two characters on either side of the cursor

Ctrl+X+Ctrl+T	Transposes the two lines on either side of the cursor

Alt+t	Transposes the two words on either side of the cursor

Esc+t	Transposes the two words on either side of the cursor

Esc+T	Transposes the two words on either side of the cursor

Alt+U	Converts a word to all uppercase

Esc+U	Converts a word to all uppercase

Esc+@u	Converts a word to all uppercase

Ins	Toggles insert mode on/off


See also

Block commands

Bookmark operations

Cursor movement


Block commands (Epsilon)

See also

These block command keyboard shortcuts apply to the Epsilon keystroke mapping scheme.

Shortcut	Action

Ctrl+Alt+\	Indents a block

Esc+Ctrl+\	Indents a block

Ctrl+X+Ctrl+I	Indents a block

Ctrl+X+Tab	Indents a block

Ctrl+W	Cuts a block and places its contents in the Clipboard

Ctrl+X+w	Writes a block to a file

Ctrl+X+W	Writes a block to a file


See also

Bookmark operations

Cursor movement

Editor


Bookmark operations (Epsilon)

See also

These bookmark operations keyboard shortcuts apply to the Epsilon keystroke mapping scheme.

Shortcut	Action

Ctrl+@	Sets a bookmark at the current cursor position

Alt+@	Sets a bookmark at the current cursor position

Esc+@@	Sets a bookmark at the current cursor position

Ctrl+2	Sets a bookmark at the current cursor position

Alt+2	Sets a bookmark at the current cursor position

Ctrl+X+.	Goes to the specified bookmark


See also

Block commands

Cursor movement

Editor


Cursor movement (Epsilon)

See also

These cursor movement keyboard shortcuts apply to the Epsilon keystroke mapping scheme.

Shortcut	Action

Ctrl+B	Moves to the left one character

Left Arrow	Moves to the left one character

Ctrl+F	Moves to the right one character

RightArrow	Moves to the right one character

Alt+m	Moves the cursor to the end of the indentation

Esc+m	Moves the cursor to the end of the indentation

Esc+M	Moves the cursor to the end of the indentation

Alt+b	Moves the cursor to the left one word

Esc+@b	Moves the cursor to the left one word

Ctrl+LeftArrow	Moves the cursor to the left one word

Alt+f	Moves to the cursor to the right one word

Esc+@f	Moves to the cursor to the right one word

Ctrl+RgAr	Moves to the cursor to the right one word

Ctrl+A	Moves to the beginning of the current line

Esc+LeftArrow	Moves to the beginning of the current line

Ctrl+E	Moves to the end of the current line

Esc+RightArrow	Moves to the end of the current line

Alt-,	Moves to the top of the current window

Esc+,	Moves to the top of the current window

Home	Moves to the top of the current window

Alt-.	Moves to the bottom of the current window

Esc+.	Moves to the bottom of the current window

End	Moves to the bottom of the current window

Ctrl+P	Moves the cursor up a line

UpAr	Moves the cursor up a line

Ctrl+N	Moves the cursor down a line

DnAr	Moves the cursor down a line

Alt+Shift-,	Goes to the start of the file

Alt+<	Goes to the start of the file

Esc+<	Goes to the start of the file

Ctrl+Home	Goes to the start of the file

Alt+Shift-.	Goes to the end of the file

Alt+>	Goes to the end of the file

Esc+>	Goes to the end of the file

Ctrl+End	Goes to the end of the file

Ctrl+V	Moves down one page in the current file

PgDn	Moves down one page in the current file

Ctrl+F6	Moves down one page in the current file

Shift+Ctrl+F6	Moves up one page in the current file

Alt+v	Moves up one page in the current file

Esc+@v	Moves up one page in the current file

PgUp	Moves up one page in the current file

Alt+Z	Scrolls the contents of the active window down a line

Esc+Z	Scrolls the contents of the active window down a line

Ctrl+Z	Scrolls the contents of the active window up a line


See also

Block commands

Bookmark operations

Editor


About keyboard shortcuts

See also

Keyboard shortcuts are two- or three-keystroke combinations you can press, while in the Code Editor, to perform a command or access a dialog box. The function of specific keyboard shortcuts depends on which keystroke mapping scheme you select.

Code Editor available keyboard mapping schemes are:

Default	Key bindings that match the CUA standard

Classic	Key bindings that match the Delphi programming environment

Brief	Key bindings that emulate most of the standard Brief keystrokes

Epsilon	Key bindings that emulate a a large part of the Epsilon editor

To select a keymapping:

1.	On the Editor display page of the Environment Options dialog box.

2.	Select a keyboard mapping scheme from the list of available schemes.

3.	Click OK.

To use SpeedSettings to set your keymappings:

1.	On the Editor options page of the Environment Options dialog box.

2.	Select a keyboard mapping scheme from the Editor SpeedSettings options.

3.	Click OK.

Note:	Using the Keystroke Mapping list box or the Editor SpeedSettings to change the mapping of your keystrokes can create conflicts with standard Windows keyboard commands.

For example, the Brief keystroke mapping defines Alt+E as File|Open, while the standard Windows action for Alt+E is to activate the Edit menu. The mapped key takes precedence so that Alt+E allows you to open a file.


See also

IDE keyboard shortcuts

Keyboard shortcuts by function

Working with the Code editor


Default keystroke mapping

The Default keystroke mapping scheme provides key bindings that match the CUA standard. For detailed information, choose one of the  topics below for a list of keyboard shortcuts:

Clipboard control

Debugger

Editor

Block commands

Bookmark operations

Cursor movement

Miscellaneous commands

System


Classic keystroke mapping

The Classic keystroke mapping scheme provides key bindings that match the Delphi programming enviroment. For detailed information, choose one of the  topics below for a list of keyboard shortcuts:

Clipboard control

Debugger

Editor

Block commands

Bookmark operations

Cursor movement

Miscellaneous commands

System


Brief keystroke mapping

The Brief keystroke mapping scheme provides key bindings that emulate the Brief editor. For detailed information, choose one of the  topics below for a list of keyboard shortcuts:

Clipboard control

Debugger

Editor

Block commands

Bookmark operations

Cursor movement

System


Epsilon keystroke mapping

The Epsilon keystroke mapping scheme provides key bindings that emulate most of the Epsilon editor. For detailed information, choose one of the  topics below for a list of keyboard shortcuts:

Clipboard control

Debugger

Editor

Block commands

Bookmark operations

Cursor movement

System


Debugger (default, classic, brief and Epsilon)

The Debugger keyboard shortcuts apply to all keystroke mapping schemes:

Default

Classic

Brief

Epsilon

Breakpoint view

Ctrl+E 	Edit Breakpoint

Ctrl+V	View Source

Ctrl+S	Edit Source

Ctrl+D	Delete Breakpoint

Ctrl+A 	Add Breakpoint

Call stack view

Ctrl+V	View Source

Ctrl+E 	Edit Source

Watch view

Ctrl+E	Edit Watch

Ctrl+A 	Add Watch

Ctrl+D	Delete Watch


Block commands (default and classic)

See also

These block command shortcuts apply to the Default and the Classic keystroke mappings schemes.

Shortcut	Action or command

Ctrl+K+B	Marks the beginning of a block

Ctrl+K+C	Copies a selected block

Ctrl+K+H	Hides/shows a selected block

Ctrl+K+I	Indents a block by the amount specified in the Block Indent combo box on the Editor options page of the Environment Options dialog box

Ctrl+K+K	Marks the end of a block

Ctrl+K+L	Marks the current line as a block

Ctrl+K+N	Changes a block to uppercase

Ctrl+K+O	Changes a block to lowercase

Ctrl+K+R	Reads a block from a file

Ctrl+K+T	Marks a word as a block

Ctrl+K+U	Outdents a block by the amount specified in the Block Indent combo box on the Editor options page of the Environment Options dialog box.

Ctrl+K+V	Moves a selected block

Ctrl+K+W	Writes a selected block to a file

Ctrl+K+Y	Deletes a selected block

Ctrl+O+C	Marks a column block

Ctrl+O+I	Marks an inclusive block

Ctrl+O+K	Marks a non-inclusive block

Ctrl+O+L	Marks a line as a block

Ctrl+Q+B	Moves to the beginning of a block

Ctrl+Q+K	Moves to the end of a block


See also

Bookmark operations

Cusor movement

Editor (classic)

Editor (default)

Miscellaneous commands


Bookmark operations (default and classic)

See also

The following bookmark operations shortcuts apply to the Default and the Classic keystroke mapping schemes.

Shortcut	Action

Ctrl+K+0 	Sets bookmark 0

Ctrl+K+1 	Sets bookmark 1

Ctrl+K+2 	Sets bookmark 2

Ctrl+K+3 	Sets bookmark 3

Ctrl+K+4 	Sets bookmark 4

Ctrl+K+5 	Sets bookmark 5

Ctrl+K+6 	Sets bookmark 6

Ctrl+K+7 	Sets bookmark 7

Ctrl+K+8 	Sets bookmark 8

Ctrl+K+9 	Sets bookmark 9

Ctrl+K+Ctrl+0	Sets bookmark 0

Ctrl+K+Ctrl+1	Sets bookmark 1

Ctrl+K+Ctrl+2	Sets bookmark 2

Ctrl+K+Ctrl+3	Sets bookmark 3

Ctrl+K+Ctrl+4	Sets bookmark 4

Ctrl+K+Ctrl+5	Sets bookmark 5

Ctrl+K+Ctrl+6	Sets bookmark 6

Ctrl+K+Ctrl+7	Sets bookmark 7

Ctrl+K+Ctrl+8	Sets bookmark 8

Ctrl+K+Ctrl+9	Sets bookmark 9

Ctrl+Q+0 	Goes to bookmark 0

Ctrl+Q+1 	Goes to bookmark 1

Ctrl+Q+2 	Goes to bookmark 2

Ctrl+Q+3 	Goes to bookmark 3

Ctrl+Q+4 	Goes to bookmark 4

Ctrl+Q+5 	Goes to bookmark 5

Ctrl+Q+6 	Goes to bookmark 6

Ctrl+Q+7 	Goes to bookmark 7

Ctrl+Q+8 	Goes to bookmark 8

Ctrl+Q+9 	Goes to bookmark 9

Ctrl+Q+Ctrl+0	Goes to bookmark 0

Ctrl+Q+Ctrl+1	Goes to bookmark 1

Ctrl+Q+Ctrl+2	Goes to bookmark 2

Ctrl+Q+Ctrl+3	Goes to bookmark 3

Ctrl+Q+Ctrl+4	Goes to bookmark 4

Ctrl+Q+Ctrl+5	Goes to bookmark 5

Ctrl+Q+Ctrl+6	Goes to bookmark 6

Ctrl+Q+Ctrl+7	Goes to bookmark 7

Ctrl+Q+Ctrl+8	Goes to bookmark 8

Ctrl+Q+Ctrl+9	Goes to bookmark 9

These shortcuts apply only to the Default scheme:

Shortcut	Action

Shift+Ctrl+0	Sets bookmark 0

Shift+Ctrl+1	Sets bookmark 1

Shift+Ctrl+2	Sets bookmark 2

Shift+Ctrl+3	Sets bookmark 3

Shift+Ctrl+4	Sets bookmark 4

Shift+Ctrl+5	Sets bookmark 5

Shift+Ctrl+6	Sets bookmark 6

Shift+Ctrl+7	Sets bookmark 7

Shift+Ctrl+8	Sets bookmark 8

Shift+Ctrl+9	Sets bookmark 9

Ctrl+0	Goes to bookmark 0

Ctrl+1	Goes to bookmark 1

Ctrl+2	Goes to bookmark 2

Ctrl+3	Goes to bookmark 3

Ctrl+4	Goes to bookmark 4

Ctrl+5	Goes to bookmark 5

Ctrl+6	Goes to bookmark 6

Ctrl+7	Goes to bookmark 7

Ctrl+8	Goes to bookmark 8

Ctrl+9	Goes to bookmark 9


See also

Block commands

Cursor movement

Editor (classic)

Editor (default)

Miscellaneous commands


Cursor movement (default and classic)

See also

These cursor movement shortcuts apply to the Default and the Classic keystroke mapping schemes.

Shortcut	Action

Ctrl+Q+B	Moves to the beginning of a block

Ctrl+Q+C	Moves to end of a file

Ctrl+Q+D	Moves to the end of a line

Ctrl+Q+E	Moves to the top of the window

Ctrl+Q+K	Moves to the end of a block

Ctrl+Q+P	Moves to previous position

Ctrl+Q+R	Moves to the beginning of a file

Ctrl+Q+S	Moves to the beginning of a line

Ctrl+Q+T	Moves to the top of the window

Ctrl+Q+U	Moves to the bottom of the window

Ctrl+Q+X	Moves to the bottom of the window


See also

Block commands

Bookmark operations

Editor (classic)

Editor (default)

Miscellaneous commands


Miscellaneous commands (default and classic)

See also

These miscellaneous commands shortcuts apply to the Default and the Classic keystroke mapping schemes.

Shortcut	Action or command

Ctrl+K+D	Accesses the menu bar

Ctrl+K+E	Changes a word to lowercase

Ctrl+K+F	Changes a word to uppercase

Ctrl+K+S	File|Save

Ctrl+Q+A	Search|Replace

Ctrl+Q+F	Search|Find

Ctrl+Q+Y	Deletes to the end of a line

Ctrl+Q+[ 	Finds the matching delimiter (forward)

Ctrl+Q+Ctrl+[	Finds the matching delimiter (forward)

Ctrl+Q+] 	Finds the matching delimiter (backward)

Ctrl+Q+Ctrl+]	Finds the matching delimiter (backward)

Ctrl+O+A  	Open file at cursor

Ctrl+O+B  	Browse symbol at cursor

Ctrl+O+G  	Search|Go to line number

Ctrl+O+O  	Inserts compiler options and directives

Ctrl+O+U	Toggles case


See also

Block commands

Bookmark operations

Cursor movement

Editor (classic)

Editor (default)


Keyboard shortcuts by function

See also

Chose one of these topics for shortcuts for some common menu commands. The shortcuts are different for each keystroke mapping scheme.

Build commands

Debug commands

Edit commands

File commands

Search commands


See also

Keyboard shortcuts

Working with the Code editor


Keyboard shortcuts for the File menu

See also

The table below lists keyboard shortcuts for file commands.

Command	Shortcut	Mapping

File|Open	F3	Classic

Alt+E 	Brief

Ctrl+X+Ctrl+F	Epsilon

Open File At Cursor	Ctrl+O+A	Default, Classic, Brief

File|Save	Ctrl+K+S	Default, Classic

Ctrl+S	Default

F2	Classic

Alt+W	Brief

Ctrl+X+Ctrl+S	Epsilon

Ctrl+X+Ctrl+W	Epsilon

File|Save As	Alt+O 	Brief

Ctrl+F7	Epsilon

Ctrl+X+s	Epsilon

Ctrl+X+S	Epsilon

File|Close	Alt+F3	Classic

Ctrl+Hyphen	Brief

Control|Close	Alt+F4	Default, Classic, Brief, Epsilon

File|Exit	Alt+X	Classic, Brief

File menu	Alt+Z	Brief

For more information about Delphi's keystroke mapping schemes, choose one of the following topics:

Default keystroke mapping

Classic keystroke mapping

Brief keystroke mapping

Epsilon keystroke mapping


See also

Build commands

Debug commands

Edit commands

Keyboard shortcuts

Search commands


Keyboard shortcuts for the Edit menu

See also

The table below lists the keyboard shortcuts for commands on the Edit menu.

Command	Shortcut	Mapping

Edit|Cut	Shift+Del	Default, Classic

Ctrl+X	Default

Minus (-)	Brief

Edit|Copy	Ctrl+Ins	Default, Classic

Ctrl+C	Default

Plus (+)	Brief

Alt+w	Epsilon

Esc+@w	Epsilon

Ctrl+Alt+w	Epsilon

Esc+Ctrl+w	Epsilon

Edit|Paste	Shift+Ins	Default, Classic

Ctrl+V	Default

Ins	Brief

Ctrl+Y	Epsilon

Edit|Redo	Ctrl+Shift+Z	Default

Alt+Shift+Backspace	Default, Classic

Ctrl+U	Brief

Ctrl+X+r	Epsilon

Ctrl+X+R	Epsilon

F10	Epsilon

Ctrl+F10	Epsilon

Ctrl+X+Ctrl+R	Epsilon

Edit|Undo	Alt+Backspace	Default, Classic

Star	Brief

Alt+U	Brief

Ctrl+X+u	Epsilon

Ctrl+X+U	Epsilon

F9	Epsilon

Ctrl+F9	Epsilon

Ctrl+X+Ctrl+U	Epsilon

For more information about Delphi's keystroke mapping schemes, choose one of the following topics:

Default Keystroke Mapping

Classic Keystroke Mapping

Brief Keystroke Mapping

Epsilon Keystroke Mapping


See also

Build commands

Debug commands

File commands

Keyboard shortcuts

Search commands


Search commands keyboard shortcuts

See also

The table below lists the keyboard shortcuts for commands on the Search menu.

Command	Shortcut	Mapping

Find	Ctrl+Q+F	Default, Classic

Ctrl+F	Default

F5	Brief

Alt+F5	Brief

Alt+S	Brief

Ctrl+Alt+S	Epsilon

Esc+Ctrl+S	Epsilon

Ctrl+Alt+R	Epsilon

Esc+Ctrl+R	Epsilon

Replace	Ctrl+Q+A	Default, Classic

Ctrl+R 	Default

Alt+T	Brief

F6	Brief

Alt+F6	Brief

Alt+&	Epsilon

Esc+&	Epsilon

Alt+%	Epsilon

Esc+%	Epsilon

Alt+*	Epsilon

Esc+*	Epsilon

Search Again	F3	Default

Ctrl+L	Classic

Shift+F5	Brief

Go To Line Number	Ctrl+O+G	Default, Classic, Brief

Alt+G	Brief

Ctrl+X+g	Epsilon

Ctrl+X+G	Epsilon

For more information about Delphi's keystroke mapping schemes, choose one of the following topics:

Default Keystroke Mapping

Classic Keystroke Mapping

Brief Keystroke Mapping

Epsilon Keystroke Mapping


See also

Build commands

Debug commands

Edit commands

File commands

Keyboard shortcuts


Debug commands keyboard shortcuts

See also

The table below lists the keyboard shortcuts for debug operations.

Command	Shortcut	Mapping

Run|Go to Cursor	F4	Default, Classic

Run|Toggle Breakpoint	F5	Default

Run|Trace Into	F7	Default, Classic

Run|Step Over	F8	Default, Classic, Epsilon

Run|Program Reset	Ctrl+F2	Default, Classic, Brief, Epsilon

Run|Add Watch	Ctrl+F5	Epsilon

Add Watch at Cursor	Ctrl+F5	Default

Ctrl+F7	Classic

Alt+F2	Brief

Browse Symbol at Cursor	Ctrl+O+B	Default, Classic, Brief

Evaluate/Modify	Ctrl+F7	Default, Brief

Ctrl+F4	Classic

Toggle Breakpoint	Ctrl+F8	Classic, Brief

F5	Epsilon

Run to Cursor	F4	Classic

Run to Cursor	Alt+F7	Brief

For more information about Delphi's keystroke mapping schemes, choose one of the following topics:

Default Keystroke Mapping

Classic Keystroke Mapping

Brief Keystroke Mapping

Epsilon Keystroke Mapping


See also

Build commands

Edit commands

File commands

Keyboard shortcuts

Search commands


Build commands keyboard shortcuts

See also

This table lists the keyboard shortcuts for build operations:

Command	Shortcut	Mapping

Project|Compile	Ctrl+F9	Default, Classic, Brief

Alt+F9	Default, Classic, Epsilon

Alt+F10	Brief

Ctrl+X+m	Epsilon

Ctrl+X+M	Epsilon

Run|Run	F9	Default, Classic, Brief, Epsilon

For more information about Delphi's keystroke mapping schemes, choose one of the following topics:

Default Keystroke Mapping

Classic Keystroke Mapping

Brief Keystroke Mapping

Epsilon Keystroke Mapping


See also

Debug commands

Edit commands

File commands

Keyboard shortcuts

Search commands


Keyboard support in the Delphi IDE

See also

Delphi IDE keyboard shortcuts are two- or three-keystroke combinations you can press to perform a command or access a dialog box directly without having to open any menu. To learn about shortcuts in the Code Editor window see Keyboard shortcuts.

To learn about shortcuts in the other windows, select one of the topics listed below:

Form keyboard shortcuts

Project Manager keyboard shortcuts

Object Inspector keyboard shortcuts

Browser keyboard shortcuts


See also

Keyboard shortcuts by function

Working with the Code editor


Form keyboard shortcuts

See also

Listed below are keyboard shortcuts for working with forms.

The IDE supports the movement and resizing of components on a form using the keyboard. The following table shows the keystrokes for selection and move and resize operations. Remember that you must select a component in order to move or resize it.

Keyboard command	Description

Tab	Selects components (in z-order)

Shift+Tab	Selects the previous component (in z-order)

Arrow keys	Selects the nearest component in the direction pressed

Ctrl+arrow keys	Moves the selected component one pixel at a time

Shift+arrow keys	Moves the selected component one pixel at a time

Ctrl+Shift+arrow keys	Moves the selected component one grid at a time (when Snap to Grid is enabled)

Del	Deletes the selected component

Esc	Selects the containing group (usually the form or group box)

F11	Toggles control between the Object Inspector and the last active form or unit

F12	Toggles between the form and its associated unit

Ctrl+F12	Displays the View Unit dialog box

Shift+F12	Displays the View Form dialog box

To add components to a form using the keyboard,

1.	Press Alt+V+L to display the Component List dialog box

2.	Type the first letter of the name of the component you want to place on the form or press Tab. Then you can use the arrow keys to scroll through the list and make a selection.

3.	Press Alt+A or Enter to add the component to the form. Pressing Enter will close the Component List dialog box.

Keys to navigate in the component list

Home	Displays the first component in the list

End	Displays the last component in the list

To change properties of a component using the keyboard,

1.	Select the component you want to modify using Tab or the arrow keys.

2.	Press Enter to switch to the Object Inspector.

3.	Use the arrow keys to select the property you want to change.

4.	Type the new value for that property and press Enter.

5.	To return to the form, press Alt+V+F and select it from the list.


See also

Keyboard shortcuts


Project Manager keyboard shortcuts

Listed below are keyboard shortcuts for working with the Project Manager.

Keyboard command	Description

Arrow keys	Selects forms and units

Alt+A	Adds a form or unit to the project

Alt+R	Removes a form or unit from the project

Alt+U	Views the selected unit

Alt+F	Views the selected form

Alt+O	Displays the Project Options dialog box

Alt+D	Updates the current project

Enter	Views the selected unit

Shift+Enter	Views the selected form

Ins	Adds a file to the project

Del	Removes a file from the project


Object Inspector keyboard shortcuts

Listed below are keyboard shortcuts for working with the Object Inspector.

Keyboard command	Description

Ctrl+I	Opens the Object Selector

Up and Down Arrow keys	Selects properties or handlers

Left and Right Arrow keys	Edits the value in the value or event column

Tab	Toggles between the property and value columns in the Object Inspector

Tab+<letter>	Jumps directly to the first property beginning with the letter

Ctrl+Tab	Toggles between the properties and events tabs in the Object Inspector

Page Up	Moves up one screen of properties

Page Down	Moves down one screen of properties

Alt+F10	Toggles expand and contract

Alt+Down	Opens a drop-down list for a property.

Ctrl-Down	Opens the object list drop-down.

Ctrl-Enter	Selects the ellipsis button (if available) in a selected property.

To change properties of a component using the keyboard,

1.	Select the component you want to modify using Tab or the arrow keys.

2.	Press Enter to switch to the Object Inspector.

3.	Use the arrow keys to select the property you want to change.

4.	Type the new value for that property and press Enter.

5.	To return to the form, press Alt+V+F and select it from the list.


Component menu

The options on the Component menu are:

New Component	Opens the Component Expert

Install Component	Install a component into an existing or new package

Import ActiveX Control	Add type libraries of ActiveX controls to your Delphi project.

Create Component Template	Customize a component and save it as a template with a new name, palette page, and icon.

Install Packages	Specify packages required by your project.

Configure Palette	Opens the Palette dialog box


Component|New
Component | New Component

See also

Use the New Component dialog to create the basic unit for a new component.

Ancestor Type 	Use the drop-down list to select a base class, or enter the name of a base class for your new component. Unless you override them in the component declaration, your new component will inherit all the properties, methods, and events from its ancestor class.

After you enter a base class, default entries are written to the Class Name and Unit File Name. You can accept or edit these entries.

Class Name	The name of the new class you are creating. A general rule is that all Object Pascal classes are prefaced with a T. For example, the name of your new button component could be TMYBUTTON.

Palette Page 	Use the drop-down list to select a page, or enter the name of the page on which you want your new component to appear on the Component palette.

Unit File Name 	The name of the unit that will contain the new component. You can include a directory path with the name; otherwise, the unit will be created in the current directory. If the unit directory is not in the Search Path, it will be added at the end.

Search Path 	The path Delphi will use to search for a file.

Install 	Install component to a new or existing package. After identifying a package, the Package Editor dialog box will be displayed. This button is not available when the New Component dialog is invoked from the Package editor.

Create Unit 	Display the component code in the Code Editor. The unit will not be included in the current project unless explicitly added. This button is not available when the New Component dialog is invoked from the Package editor.


Component | Install Component

See Also

Select Component | Install Component to install a component into an existing or new package.

After entering the information required, press OK.  The Package editor dialog box is displayed.

Into existing package

In this case, you write the component code, identified by the unit, name to an existing package file.

Into new package

In this case, you declare a new package and write the component code, identified by unit name, to it.


Into new package

Unit file name 	Enter the name of the unit you want to install. If the unit is in the Search Path, a full path name is not required. If the unit directory is not in the Search Path, it will be added to the end.

Search path 	The path used by Delphi to search for files.

Package file name 	Enter the name of the package to create. You can include a directory path with the name; otherwise, the package will reside in the current directory. To open a file/directory selection dialog box, click Browse. If you type a file name directly in the New Package dialog box, the .DPK extension will be added automatically.

Package names must be unique within a project. If you name a package “STATS”, the Package editor generates a source file for it called “STATS.DPK”; the compiler generates an executable and a binary image called “STATS.DPL” and “STATS.DCP”, respectively. Use “STATS” to refer to the package in the requires clause of another package, or when using the package in an application.

Package description 	A brief description of the package.


Into existing package

Unit file name 	Enter the name of the unit you want to install. If the unit is in the Search Path, a full path name is not required. If the unit directory is not in the Search Path, it will be added to the end.

Search path 	The path used by Delphi to search for files.

Package file name 	Use the drop-down list to select the name of an installed package, or enter the name of another existing package.

Package description 	A brief description of the selected package.


See also

About Packages


Component | Import ActiveX Control

See also

The Import ActiveX Control dialog displays the ActiveX controls registered on your system so you can add them to your Delphi projects. You can generate Pascal declarations in a .PAS file that let you use any of these controls as though it were a native VCL object. In effect, the ActiveX control is placed within a Delphi wrapper.

The top part of the dialog is a list of Controls that are currently registered and thus available to be imported into Delphi. This lists lets you extract the Pascal declarations from an existing control. You can also conveniently register a new control from this dialog box so that it is available to be imported.

To add and register a new ActiveX control:

1.	Click Add. The Register OLE Control dialog box appears.

2.	In the Register OLE Control dialog box, navigate to the disk or network location of the control file you want to add.

3.	Select the new ActiveX control. It is automatically registered on your system for Delphi and immediately appears in the list of available controls in the Import ActiveX Control dialog.

Add button 	The Add button lets you locate a new ActiveX control and register it in the Windows Registry, so that it will appear in the list of registered objects available to be imported into Delphi.

Remove button 	To remove a registered ActiveX control, click the Remove button. The control is removed from the Windows Registry and from this list.

Class names 	Shows only the ActiveX control classes in the selected library.

Palette page 	Shows the Component palette location of objects associated with the selected library. Allows you to group controls by function or vendor, for example.

Unit dir name 	Shows the name of the directory that will contain the unit using this control. Only the path root is shown; no file name appears. The unit name is derived from the internal type library name. Click the Browse button to move up the directory tree. If the Unit Directory name is not in the Search Path, it will be added to the end.

Search path 	Shows the path Delphi will use to search for a file.

Install…	Creates a unit, opens the Package editor, and installs the unit in the package you specify. When you click Install, the Package editor appears, asking if you want to install the new unit in the default Delphi package or create another package to contain it. This button is not available when the dialog is invoked from the Package editor.

Create unit 	Creates a unit and displays the unit code in the Code editor. (Does not include the unit in the current project.) This button is not available when the dialog is invoked from the Package editor.


See also

ActiveX controls


Component | Create Component Template

See also

To create a custom component template:

1.	Choose an existing component as a starting point and place it on a form.

2.	With the component selected, in the Object Inspector, set the properties and events you want for your template.  For example, if you are creating a StaticText component to be used on forms that require a standard appearance, set the component’s Font and Color properties.

3.	With the desired component still selected, choose Component|Create Component Template to give your custom template a new name, palette location, and icon.

Component Name 	This field shows the name of the currently selected component and the appended suffix, “template”.

Palette Page 	Click to select the tabbed Delphi palette on which you want the new component to appear.

Palette Icon 	This field shows the icon of the currently selected component.  To change it, click the Change button, navigate the Open dialog to find a directory of images, and choose a new image for the icon.

To create the new component template, click OK.  Your new template appears immediately in the tabbed pallet page you indicated with the new icon.


See also

Creating and using component templates


Database menu

The Database menu commands enable you to create, modify, and view your databases.

Explore

SQL Monitor

Form Wizard


Database | Explore

Choose Database|Explore to open the Database Explorer or SQL Explorer, depending on your version of Delphi. Both tools let you create, view, and edit data and BDE aliases. In addition, the SQL Explorer lets you query local and remote databases.


Database | SQL Monitor

Choose Database|SQL Monitor to open the SQL Monitor.

This tool, available only in some versions of Delphi, lets developers monitor SQL resource allocation and other SQL access information for an application.


Database | Form Wizard

See also

Choose Database|Form Wizard to use the Delphi Form Wizard to create a form that displays data from a local or remote database.


See also

Using the Form Wizard


Using the Form Wizard

See also

Use the Form Wizard to easily generate a form that displays data from an InterBase, Paradox, dBASE, or Oracle database.

Select the type of database forms to create:

Simple database form

Master/detail form

Select the DataSet option:

TTableObject

TQueryObject

The tool automates such form building tasks as:

Connecting the form to Table and Query components

Writing SQL statements for Query components

Placing interactive and non-interactive components on a form

Defining a tab order

Connecting DataSource components to interactive components and Table/Query components


See also

Creating a form using the Form Wizard


Creating a form using the Form Wizard

You can use the Form Wizard to create a simple database form.

To build a database form by using the Form Wizard:

1.	Open the Form Wizard by choosing Database|Form Wizard.

2.	Select a Form Option.

3.	Select a DataSet and click Next.

4.	From the Drive or Alias Name list, select an alias.

Note:	If you have not created an alias, you can still enter a database name by specifying the path to a database in the Form Wizard dialog box.

5.	Select the fields to use on the generated form.

To use only some of the fields:

1.	Press and hold Ctrl.

2.	Select each field you want from the Available Fields list.

3.	Choose the > button.

To use all of the fields from the Available Fields list:

Click the button marked >>.

To remove fields from the Selected Fields list:

Click the buttons marked < or <<.

To reorder the fields in the Selected Fields list:

1.	Select a field to move.

2.	To change the field's position in the list, choose the Up or Down button.

For the purposes of this exercise, use all the fields from the Available Fields list. Choose Next to proceed.

3.	The next Form Wizard screen presents options for displaying the selected fields on the form. The Expert explains and illustrates each of your choices.

For the purposes of this exercise, choose the Vertical option.

4.	The Form Wizard generates text labels for each of the data entry components in the generated form when you opt for a vertical layout. You can choose the way these labels are displayed in relation to the data entry fields. The screen explains and illustrates your choices.

For this exercise, choose the Left option, then choose Next to proceed.

5.	Choose the Create button to generate the form.

The Form Wizard generates a database form based on your choices.


Edit menu

Edit commands keyboard shortcuts

Use the Edit menu commands to manipulate text and components at design time.

Undelete (Undo) 	Undoes your last action or last deletion

Redo	Reverses an undo

Cut	Removes a selected item and places it on the Clipboard

Copy	Places a copy of the selected item on the Clipboard, leaving the original in place

Paste	Copies the contents of the Clipboard into the Code Editor window or form

Delete	Removes the selected item

Select All	Selects all the components on the form

Align To Grid	Aligns the selected components to the closest grid point

Bring To Front	Moves the selected component to the front8

Send To Back	Moves the selected component to the back

Align	Aligns components

Size	Resizes components

Scale	Resizes all the components on the form

Tab Order	Modifies the tab order of the components on the active form

Creation Order	Modifies the order in which nonvisual components are created

Lock Controls	Secures all components on the form in their current position

Add to interface	Define a new method, event, or property for an ActiveX component.


Edit|Undo
Edit | Undo (Undelete)

See also

Choose Edit|Undo (Undelete) in the Code Editor to undo your most recent keystrokes or mouse actions. Choose Edit|Undo when working with a form to replace an item you just deleted.

Using Undo in the Code Editor

Undo can reinsert any characters you delete, delete any characters you insert, replace any characters you overwrite, or move your cursor back to its prior position.

You can undo multiple successive actions by choosing Undo repeatedly. This removes your changes by "stepping back" through your actions and reverting them to their previous state. You can specify an undo limit on the Editor Options page of the Project|Environment dialog box.

If you undo a block operation, your file appears as it was before you executed the block operation.

The Undo command does not change an option setting that affects more than one window.

Check Group Undo on the Editor Options page of the Options|Environment dialog box to undo a group of actions.


See also

Edit|Redo

Keyboard shortcuts for the Edit menu


Edit | Redo

See also

Choose Edit|Redo to reverse the effects of your most recent undo.

Redo has an effect only immediately after an Undo command.

Redo is not available to replace an object deleted from a form.


See also

Edit|Undo (Undelete)

Edit commands keyboard shortcuts


Edit | Cut

See also

Choose Edit|Cut to remove the following items from their current position and place them on the Clipboard:

Selected text from the Code Editor.

Components from the form.

Menus from the Menu designer.

Cut replaces the current Clipboard contents with the selected item.

To insert the contents of the Clipboard elsewhere:
Choose Edit|Paste.


See also

Edit commands keyboard shortcuts


Edit | Copy

See also

Choose Edit|Copy to place an exact copy of the selected text, component, or menu on the Clipboard and leave the original untouched. Copy replaces the current Clipboard contents with the selected items.

To paste the contents on the Clipboard elsewhere:
Choose Edit|Paste.


Edit | Paste

See also

Choose Edit|Paste to insert the contents of the Clipboard into the active Code Editor page, the active form, or active menu in the Menu designer.

Note: You can paste only text into the Code Editor window, only components onto the form, and only menu items into the Menu designer.

When pasting into the Code Editor window, the text is inserted at the current cursor position.

When pasting onto the form, nonvisual components are pasted into the upper left corner of the form, and visual components are pasted into the exact position from which they were cut or copied.

When pasting into the Menu designer, menu items are inserted at the cursor position.

You can paste the current contents of the Clipboard as many times as you like until you cut or copy a new item onto the Clipboard.


See also

Edit|Copy

Edit|Cut

Edit commands keyboard shortcuts


Edit | Delete

Choose Edit|Delete to remove the selected text or component without placing a copy on the Clipboard.

Even though you cannot paste the deleted text, you can restore it by immediately choosing Edit|Undo (Undelete).

Delete is useful if you want to remove an item but you do not want to overwrite the contents of the Clipboard.


Edit | Select All

Choose Edit|Select All to select every component on the active form. When you select all the components only those properties which the components have in common will appear in the Object Inspector.


Edit | Align To Grid

Choose Edit|Align To Grid to align the selected components to the closest grid point.

You can specify the grid size on the Preferences page of the Tools| Environment Options dialog box.


Edit | Bring To Front

See also

Choose Edit|Bring To Front to move a selected component in front of all other components on the form. This is called changing the component's z-order.

Note:	The Bring To Front and Send To Back commands do not work if you are combining windowed and non-windowed components. For example, you cannot change the z-order of a label in relation to a button.


Edit|Sent to Back
Edit | Send To Back

See also

Choose Edit|Send To Back to move a selected component behind all other components on the form. This is called changing the component's z-order.

Note:	The Send To Back and Bring To Front commands do not work if you are combining windowed and non-windowed components. For example, you cannot change the z-order of a label in relation to a button.


See also

SendToBack method


Edit | Align

See also

Choose Edit|Align to open the Alignment dialog box.

Alignment dialog box

Use this dialog box to line up selected components in relation to each other or to the form.

The Horizontal alignment options align components along their right edges, left edges, or midline.

The Vertical alignment options align components along their top edges, bottom edges, or midline.

The options for horizontal or vertical aligment are:

Option	Description

No Change	Does not change the alignment of the component

Left Sides	Lines up the left edges of the selected components (horizontal only)

Centers	Lines up the centers of the selected components

Right Sides	Lines up the right edges of the selected components (horizontal only)

Tops	Lines up the top edges of the selected components (vertical only)

Bottoms	Lines up the bottom edges of the selected components (vertical only)

Space Equal	Lines up the selected components equidistant from each other

Center In Window	Lines up the selected components with the center of the window


See also

Alignment Palette

Aligning components


Edit | Size

Choose Edit|Size to open the Size dialog box.

Size dialog box

Use this dialog box to resize multiple components to be exactly the same height or width.

The Width options change the horizontal size of the selected components.

The Height options align the vertical size of the selected components.

The options for horizontal or vertical sizing are:

Option	Description

No Change	Does not change the size of the components.

Shrink To Smallest	Resizes the group of components to the height or width of the smallest selected component.

Grow To Largest	Resizes the group of components to the height or width of the largest selected component.

Width	Sets a custom width for the selected components.

Height	Sets a custom height for the selected components.


Edit | Scale

Choose Edit|Scale to open the Scale dialog box.

Scale dialog box

Use this dialog box to proportionally resize the form and all the components on that form.

Scaling Factor, In Percent

Enter a percentage to which you want to resize the form.

Percentages over 100 grow the form.

Percentages under 100 shrink the form.


Edit | Tab Order

See also

Choose Edit|Tab Order to open the Edit Tab Order dialog box.

Use this dialog box to modify the tab order of the components on the form or within the selected component if that component contains other components.

Controls	Lists the components on the active form in their current tab order. The first component listed is the first component in the tab order. The default tab order is determined by the order in which you placed the components on the form.

Up	Click Up to move the component selected in the Controls list box higher in the tab order.

Down	Click Down to move the component selected in the Controls list box lower in the tab order.

To change the tabs order of a component:

1.	Select the component name.

2.	Click the up button to move the component up in the tab order, or click the down arrrow to move its down in the tab order.

You can also drag the selected component to its new position in the tab order.

3.	To save your changes, click OK.


See also

Setting tab order


Edit | Creation Order

Choose Edit|Creation Order to open the Creation Order dialog box.

Creation Order dialog box

Use this dialog box to specify the order in which your application will create nonvisual components. when you load the form at design time or run time.

The list box displays only those nonvisual components on the active form, their type, and their current creation order. The default creation order is determined by the order in which you placed the nonvisual components on the form.

To change the creation order:

1.	Select a component name.

2.	Click the up button to move the component creation order up, or click the down arrrow to move its creation order down.

You can also drag the selected component to its new position in the creation order.

3.	To save your changes, click OK.


Edit | Lock Controls

Choose Edit|Lock Controls to secure all components on the active form in their current position. When this command is checked, you cannot move or resize a control. However, you can use the Object Inspector to edit the Height, Left, Top, and Width properties for a selected control.

When this command is checked, controls are locked. When controls are locked, you can choose Lock Controls to unlock them.

Note:	Lock Controls has no effect on the form, itself. When you select Lock Controls, you can still resize or move the form.


Add To Interface
Edit | Add to Interface

See also

Choose Edit|Add to Interface to define a new procedure, function, or property for an ActiveX component.  These elements will be added to the ActiveX component’s interfaces, making them available to other applications. This command is a handy short-cut for declaring an interface member to be used by an ActiveX component.

Alternatively, you can right-click in an ActiveX implementation file and choose Add to Interface from the speedmenu.

The Edit|Add to Interface command displays the Add to Interface dialog box that lets you choose an interface member type (such as property, method, or event) and then quickly enter the declaration for the selected type.

To automatically check the syntax of what you type in the declaration box,
Click the Syntax Helper checkbox.

When you click OK, the declaration you entered is automatically stored in the three required locations:

The current ActiveX Implementation unit

The ActiveX Type Library (TLB file)

The Delphi Type Library (DTL file)

Now you can simply write the actual Pascal code for the method or set the property in your implementation unit.


See also

Edit commands keyboard shortcuts


File menu

See also

Use the File menu to open, save, close, and print new or existing projects and files, and to add new forms and units to the open project.

File commands	Description

New	Opens the New Items dialog box which contains new items that can be created.

New Application	Creates a new project containing a form, a unit, and a .DPR file.

New Form	Creates and adds a blank form to the project.

New Data Module	Creates and adds a new, blank data module form to the project.

Open	Use the Open dialog box to load an existing project, form, unit, or text file into the Code editor.

Reopen	Displays a cascading menu containing a list of most recently closed projects and modules.

Save	Saves the current file using its current name.

Save As	Saves the current file using a new name, including modifications made to project files

Save Project As	Saves the current project using a new name.

Save All	Saves all open files, both current project and modules.

Close	Closes the current project and its associated units and forms.

Close All	Closes all open files.

Use Unit	Adds the selected unit to the uses clause of the active module.

Add to Project	Adds an existing file to the project.

Remove From Project	Removes a file from the project.

Print	Sends the active file to the printer.

Exit	Closes the open project and exits Delphi.


See also

File commands keyboard shortcuts


File | New

See also

The New Items dialog box provides access to the templates and the Forms, Dialogs, Projects, and Business pages of the Delphi Object Repository. The New Items dialog box contains pages for each category of template or Object Repository page.

The Object Repository contains forms, projects, and Wizards. You can use the objects directly, copy them into your projects, or inherit from existing objects, depending on the type of object. For example, you can only copy from Wizards.


See also

File|Save

File|Open

Managing Projects

Tools|Repository


File | New Application

Choose File|New Application to create a new Delphi project.

In addition to the standard blank project, you can specify a custom project as the default project.

You can have only one project open at any time. If a project is open when you choose File|New Project, Delphi prompts you to save any changes made to the current project.

A new project consists of:

a new project file (PROJECT1.DPR)

a new form file (FORM1.DFM), and its associated form unit (UNIT1.PAS)

You can change the project and unit file names when you save them.

To redefine the default project:

1.	Choose Tools|Repository to open the Object Repository dialog box.

2.	In the Pages list box, click Projects.

A list of projects appears in the Objects list box.

3.	Select the project that you want to become the default project.

4.	Select the New Project check box.

5.	Click OK.

The default project specified in the steps above will be now be used when you use the New Application command.


File | New Form

Choose File|New Form to create a blank form and a new unit and add them to the project.

When you create a new form, Delphi adds the new form and an associated unit file to the list of files included in the open project. If no project is open, a blank form is created.

If you did not redefine the default form, the new form is titled FormXX and the new unit is UnitXX.PAS. where XX represents the form/unit number, that is, the first form is Form1, the second Form2.

To change the name of the form:
Edit the Name property from the Object Inspector.

To change the unit name:
Save the file by using File|Save File As or save the entire project by using File|Save Project As.

Changes made to any form or unit name are reflected throughout the source code anywhere that name appears within that unit.

You can specify a custom form as the default form.

To redefine the default form:

1.	Choose Tools|Repository to open the Object Repository dialog box.

2.	In the Pages list box, click Forms or Dialogs.

A list of items appears in the Objects list box.

3.	Select the form that you want to become the default form.

4.	Select the New Form check box.

5.	Click OK.

The default form specified in the steps above will be now be used when you use the New Form command.


See also

Forms, the basic building blocks


File | New Data Module

See also

Choose File|New Data Module to add a new data module to your project. A data module container appears on the IDE desktop and adds the data module to the project file.

At design time, a data module looks like a standard Delphi form with a white background and no alignment grid.  As with forms, you can place nonvisual components on a module from the Components palette, and you can resize a data module to accommodate the components you add to it.


See also

Creating a new data module

Naming a data module and its unit file

Reusing data modules in the Object Repository

Sharing objects

Copy, Inherit, or Use?

Inheriting a data module

Using a data module

Adding a data module to the Object Repository


New Items dialog box

See also

Use the New Items dialog box to select a form, project template, or Wizard that you can use as a starting point for your application. The New Items dialog box provides a view into the Delphi Object Repository. The Object Repository contains forms, projects, and Wizards. You can use the objects directly, copy them into your projects, or inherit items from existing objects.

To add a new form from the New Items dialog box:

1.	Choose File|New to open the New Items dialog box.

2.	Choose the tab that contains the item you want add from the Forms or Dialogs page.

3.	Select the item in the list view that represents the kind of form you want to add.

4.	Choose whether you want to copy, inherit, or use the new form.

To start a new project from a project template:

Choose File|New to display the New Items dialog box.

1.	Choose the Projects tab.

2.	Select the project template you want and choose OK. Or select the Application Wizard to define a custom template.

3.	In the Select Directory dialog box, specify a directory for the new project’s files.

A copy of the project template opens in the specified directory. Or the Application Wizard will prompt you to enter a directory name.

To view the item description:

1.	From the File menu select New.

2.	Select an item in the New Items dialog box.

3.	Right-click the mouse.

4.	Select View Details from the context menu.

The item description appears in the Description column.

The New Items dialog box pages

When you first install Delphi, there are seven tabs in the New Items dialog box. Each page contains items that you can include in your project.  Three of these pages are fixed, providing standard Delphi components:

New

ActiveX

Your project

The remaining pages are user-defined pages containing forms, projects, data modules, or Wizards from the Object Repository.  When shipped, the New Items dialog box contains the pages

Forms

Dialogs

Data Modules

Projects

Right-click the page and choose View Details to read a description of each item.

The New Items context menu

Right-click the New Items dialog box to display the speed menu. The speed menu allows you to customize the display.

Note:	To open the Object Repository dialog box, select Properties from the context menu. You can use this dialog box to edit, add pages to, and rename items in the Object Repository.

Usage Options

There are three ways to include a Repository Object in your project. The options are dimmed if unavailable for a specific object.

Copy the item

Inherit from the item

Use the item directly

Copying Items

When you copy an item, you make an exact duplicate of the item and add it to your project if it is a form or data module. Any changes to the item in the Object Repository will not be reflected in your copy. Alterations you make to your copy will not affect the original Repository item.

Note: 	Copying is the only option available for using project templates.

The copy option is the only option for form or project Wizards. Using an Wizard does not add shared code; it runs a process that generates its own code.

Inheriting Items

The most flexible and powerful option is to inherit an item. When you inherit an item, a new class is derived from the item and is added to your project. When you recompile your project, any changes made to the item in the Object Repository will be reflected in your derived class, unless you have changed a particular aspect. Changes made to your derived class do not affect the shared item in the Object Repository.

Note:	Inheriting is available as an option for forms, dialog boxes, and data modules but not for project templates. It is the only option available for reusing items from within the same project.

Using Items Directly

You use the Using Items Directly option primarily with data modules. When you use an item directly, the item is added to your project as if you had created it as part of the project. Design-time changes made to the item appear in all projects that directly use the item as well as any projects that inherit from the item.

Note:	Using Items Directly is an option for forms, dialog boxes, and data modules. These items should be modified only at run time to avoid making changes that affect other modules.


See also

Tools|Repository


Remote Dataset Wizard

See also

Use the Remote Dataset Wizard to create a data module that can be accessed remotely using OLE automation.  A remote data module resides in the application server between a client and server in a multitiered database environment.  The Remote Dataset Wizard allows you to specify the class name of the object that implements the OLE interface clients will use to access the remote data module

To bring up the Remote Dataset Wizard:

1	Choose File|New to open the New Items dialog box.

2	Choose the tab labeled New.

3	Select the Remote Data Set item in the list view.

The Remote Dataset Wizard Options

Class Name edit control

Enter the class name of the object that implements the OLE automation interface for your remote data module.  The class name for your remote data module will be this name with a T prepended.  To enable a client application to access this remote data module, set the ServerName property of the client application’s MIDAS connection component to this class name.

Instancing combo box

Choose what other processes can use the remote data module’s interface to control it.  The class factory that instantiates your remote data module uses the value of instancing to determine whether the remote data module should be created internal to the COM server,  or in single or multiple instances.  The following table lists the possible values:

Value	Meaning
ciInternal	The remote data module is created internally to the COM server.  The interface is available only within the same process space.
ciSingleInstance	Only a single instance of the remote data module is created.  The interface is available to processes outside the application that includes the remote data module. All clients operate on a single instance.
ciMultiInstance	Multiple instances of the remote data module can be created. .  The interface is available to processes outside the application that includes the remote data module. All clients operate on their own instance


New Web Server Application dialog box

See also

Use the New Web Server Application dialog box to specify the type of server your Web server application will work with. Choose the type of Web Server your application will use. After choosing the type of Web server application, click OK to create a new project configured to use Internet components and containing an empty Web module.

To bring up the New Web Server Application dialog box:

1	Choose File|New to open the New Items dialog box.

2	Choose the tab labeled New.

3	Select the Web Server Application item in the list view.

The New Web Server Application Options

ISAPI/NSAPI Dynamic Link Library

ISAPI and NSAPI Web server applications are DLLs that are loaded by the Web server. Client request information is passed to the DLL as a structure and evaluated by TISAPIApplication.Selecting this type of application adds the library header of the project files and required entries to the uses list and exports clause of the project file.

CGI standalone executable

A CGI standalone Web server application is a console application that receives client request information on standard input and passes the results back to the server on standard output. This data is evaluated by TCGIApplication.Selecting this type of application adds the required entries to the uses clause of the project file and adds the appropriate $APPTYPE directive to the source.

Win-CGI standalone executable

A Win-CGI standalone Web server application is a Windows application that receives client request information from a configuration settings (INI) file written by the server and writes the results to a file that the server passes back to the client. The INI file is evaluated by TCGIApplication.  Selecting this type of application adds the required entries to the uses clause of the project file and adds the appropriate $APPTYPE directive to the source.


New Thread dialog box
New Thread Object dialog box

See also

Use the New Thread Object dialog box to define a thread class that encapsulates a single execution thread in a multi-threaded application.  Type the name of the class you wish to define in the Class Name edit control.  Click OK to create a new unit that defines a thread class with the name supplied in the dialog. You must then supply the code that executes when the thread is run by writing the Execute method in the implementation section of the new unit.

Note

Unlike some other dialogs in Delphi, the New Thread Object dialog does not prepend a T to the supplied class name.  You will want to type a class name such as TMyThread, rather than typing MyThread and expecting the T to be added implicitly.

To bring up the New Thread Object dialog box:

1	Choose File|New to open the New Items dialog box.

2	Choose the tab labeled New.

3	Select the Thread Object item in the list view.


New

New Item	Description

Application	Creates a new project containing a form, a unit, and a .DPR, or provides a way for you to select a template.
Component	Creates a new component by using the Component Wizard.
Data Module	Creates a new data module that can be used as a repository for nonvisual components and business rules.
DLL	Creates a new DLL project.
Form	Creates and adds a blank form to the current project, or lets you select a form template.
Packages	Creates a new package.  Displays the New Package dialog box where you specify the file name and description of the new package. The new package appears in the Package editor.
Remote Data Module	Create a new remote data module, an OLE automation object that resides in the application server between a client and server in a multitiered database environment.  The Remote Dataset Wizard appears allowing you to enter a class name for the new remote data module and set the type of instance.
Text	Creates a new ASCII text file.
Thread Object	Creates a new thread object.
Unit	Creates and adds a new unit to the current project
Web Server	Creates a new web server application DLL or EXE.


ActiveX

Click the objects in this page to create new Active forms, ActiveX controls, property pages for ActiveX controls, and type libraries for Active X controls or OLE Automation objects.

New Item	Description

ActiveX Control	Create a new ActiveX control.  The ActiveX Control Wizard appears to guide you through the creation process, choosing the VCL object on which you want to base the new control.  Note that ActiveX controls need an ActiveX library to expose their interfaces and method arguments to client applications.
ActiveX Library	Create a new ActiveX library.  A template file named Project1.dpr is created as a starting point for you.  An ActiveX Library project must be open before you can add a new ActiveX control.
Automation Object	Create a new OLE Automation object.  The Automation Object Wizard appears, allowing you to enter a class name for the new automation object and set the type of instance. An ActiveX Library project must be open before you can add a new Automation object.
Property Page	Create a Unit.pas file set up as an ActiveX property page. The property page appears in design mode, ready for you to add private and public declarations.  You can design a dialog box in the form window, grouping properties to make it easy for developers to modify the control when implementing it in an application.
Active Form	Create a new Active form, which is a simpler ActiveX control (descended from TActiveForm) preconfigured to run on a web browser. The ActiveX Control Wizard appears to guide you through the creation process, allowing you to add controls to the form. The wizard creates an ActiveX Library project (if needed), a type library, a form, an implementation unit, and a unit containing corresponding type library declarations. [Unlike other ActiveX controls, you cannot modify the properties of a built Active form in a development environment unless you add code to publish the properties.]
Type Library	Create or edit a library of type information for an ActiveX control or an OLE Automation object.  The Type Library editor appears.


Your Project Name

If a project is open, the second page in the New Items dialog box is the current project page. The current project title is displayed on the tab. The current project page contains all the forms of the project. You can create an inherited form from any existing project forms.


Select Directory dialog box

Use the Select Directory dialog box to choose a working directory for your new project.

To open the Select Directory dialog box:
Select File|New.., select the Projects tab, and select a non-blank project template.

Directory Name 	Displays the current directory. If you enter a directory that does not exist, Delphi will create it.

Directories 	Lists the current directory.

Files: (*.*)	List all the files in the current directory. You cannot select any of these files. Delphi displays this file list so you know the contents of the current directory.

Drives 	Lists all the available drives. You can select one of the available drives.


File | Open

Choose File| Open to display the Open dialog box.

Open dialog box

Use the Open dialog box to load an existing project, form, unit, or text file into the Code editor.

Opening a file does not add it to your current project. To add a file to a project, choose File|Add To Project.

You can open multiple forms, units, or text files but you can have only one project open at any time.

If a project is open when you select File|Open, Delphi prompts you to save any changes made to the current project.

Look In 	Lists the current directory. Use the drop down list to select a different drive or directory.

Files 	Displays the files in the current directory that match the wildcards in File Name or the file type in Files Of Type. You can display a list of files (default) or you can show details for each file.

File Name 	Enter the name of the file you want to load or wildcards to use as filters in the Files list box. When you select a

Files of Type 	Choose the type of file you want to open; the default file type is Project file (.DPR). All files in the current directory of the selected type appear in the Files list box.

Up One Level	Click this button to move up one directory level from the current directory.

Create New Folder	Click this button to create a new subdirectory in the current directory.

List	Click this button to view a list of files and directories in the current directory.

Details	Click this button to view a list of files and directories along with time stamp, size, and attribute information.


File | Reopen

Choose File|Reopen to reopen a recently closed project or module.

When you close a project or a module, it is added to the Reopen list.

To Reopen a project or module:

1.	From the File menu, point to Reopen.

2.	Click the project or module that you want to reopen.

Note:	Only projects or modules that have been closed with the File|Close command appear in the Reopen list. Saved Items do not appear in the list.


File | Save

Choose File|Save to store changes made to all files included in the open project using the current name for each file.

If you try to save a project that has an unsaved project file or unit file, Delphi opens the Save As dialog box where you enter the new file name.

Note:	Open files that are not included in the project file will not be saved. To save these files, select each file in the Code editor and choose File|Save.


File | Save As

Choose File|Save As to save the active file with a different name or in a different location.

Save As dialog box

Use the Save As dialog box to change the project file name or to save the project in a new location. If the file name already exists, Delphi asks if you want to replace the existing file.

Look In 	Lists the current directory. Use the drop down list to select a different drive or directory.

File Name	Enter a name for the file you are saving.

Files	Displays the files in the current directory that match the file type in the Save File as Type combo box.

Save File As Type	Choose a file extension; the default is .DPR. All files in the current directory of the selected type appear in the Files list box. Note that saving a project file with a different extension does not change the format of the file.

Up One Level	Click this button to move up one directory level from the current directory.

Create New Folder	Click this button to create a new subdirectory in the current directory.

List	Click this button to view a list of files and directories in the current directory.

Details	Click this button to view a list of files and directories along with time stamp, size, and attribute information.


File | Save Project As

Choose File|Save Project As to save the project file ( .DPR file) to a new name or location. In addition to copying and/or renaming the .DPR file and associated project files, this command saves each associated file using its current location and name.

Note:	If you have modified forms or units that are used by other projects, and you do not want the current modifications reflected in those other projects, you should first use File|Save File As to copy/rename each unit file before choosing this command to save the project.

Save Project As dialog box

Use the Save Project As dialog box to change the project file name or to save the project in a new location. If the file name already exists, Delphi asks if you want to replace the existing file.

Look In 	Lists the current directory. Use the drop down list to select a different drive or directory.

File Name	Enter a name for the project file you are saving.

Files	Displays the files in the current directory that match the file type in the Save File As Type combo box.

Save File As Type	Choose a file extension; the default is .DPR. All files in the current directory of the selected type appear in the Files list box. Note that saving a project file with a different extension does not change the format of the file.

Up One Level	Click this button to move up one directory level from the current directory.

Create New Folder	Click this button to create a new subdirectory in the current directory.

List	Click this button to view a list of files and directories in the current directory.

Details	Click this button to view a list of files and directories along with time stamp, size, and attribute information.


File | Save All

Choose File|Save All to save all open files, including the current project and modules.

If the files have not been saved before:

1.	From the File menu, choose Save All.

2.	The Save All dialog box appears with a default name for the item to be saved.

3.	Type in a new file name if you do not want to use the default name.

4.	Click Save.

The Save As dialog appears again with a default name for the next item to be saved.

5.	Repeat steps 3-4 until all modules are saved.


File | Close

Choose File|Close to close the active window.

Closing a form also closes the associated unit file. Before closing the file, Delphi prompts you to save any changes. If you have not previously saved the project, or any file, Delphi opens the Save As dialog box, where you can enter the new file name.

You can close the entire project by choosing File|Close when the Project Manager is the active window. If you close the project file (.dpr)  from the Code editor, you also close the entire project.


File | Close All

Choose File|Close all to close all open files.The project file and all modules are closed.


File | Use Unit

Choose File|Use Unit to include an existing a unit in the current unit in the Code editor. This command lets you access public objects, methods, functions, and procedures in the chosen unit from the current unit.


Use Unit dialog box

Use this dialog box to make the contents of the specified unit available to the current unit.

To use a unit:

1.	From the File menu, select Use Unit.

2.	In the Use Unit list, click the unit name you want to add.

3.	Click OK to add the unit to the current unit.

Use Unit list	This list displays a list of all units in the project that are not already used by the current unit.


File | Add to Project

See also

Choose File|Add to Project to open the Add To Project dialog box.

Add To Project dialog box

Use the Add To Project dialog box to add an existing unit and its associated form to the Delphi project. When you add a unit to a project, Delphi automatically adds that unit to the uses clause of the project file.

Look In 	Lists the current directory. Use the drop down list to select a different drive or directory.

File Name	Enter the name of the file you want to load or wildcards to use as filters in the Files list box.

Files	Displays the files in the current directory that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box.

Files Of Type	Choose the type of file you want to open; the default file type is Source file (.PAS). All files in the current directory of the selected type appear in the Files list box.

Up One Level	Click this button to move up one directory level from the current directory.

Create New Folder	Click this button to create a new subdirectory in the current directory.

List	Click this button to view a list of files and directories in the current directory.

Details	Click this button to view a list of files and directories along with time stamp, size, and attribute information.


See also

Adding units and forms to a project

File|Remove from Project

Viewing and manipulating a project's contents


File | Remove From Project

See also

Choose File|Remove From Project remove a unit from the current project. .

Remove From Project dialog box

Use this dialog box to select a unit to remove from the current project. When you select a unit and click OK, Delphi removes the selected unit from the uses clause of the current project file but does not delete the file from your disk. Any references to the unit that you added must be removed manually.

If you have modified the file during this editing session, Delphi prompts you to save your changes, to preserve the unit in another project. If you have not modified the file, Delphi removes that file from the project without prompting you.

Note:	Do not delete unit files with other file management programs, and do not delete directly from the DOS prompt. Doing so will cause errors.


See also

File|Add to Project

Removing forms and units from a project

Viewing and manipulating a project's contents


File | Print

Choose File|Print to print the active page in the Code editor or the active form. When you choose File|Print, Delphi displays one of two dialog boxes depending on whether the Code editor or the form is the active window.

When the Code editor is active, Delphi displays the Print Selection dialog box.

When the form is active, Delphi displays the Print Form dialog box.


Print Form dialog box

Use this dialog box to specify any scaling options when printing a form. The scaling options depend on the size of the printer paper. You can change the size of the paper using the Paper Size option in the Printer Setup dialog box.

To display this dialog box, select File|Print when a form is active.

There are three scaling options:

Proportional: Scales the form using value of the PixelsPerInch property. Depending on the value of PixelsPerInch, your form may print on more than one page.

Print To Fit Page:  Scales the form so that it will fit onto one page.

No Scaling:  Prints the form using its current onsceen size. If you choose this option, your form might print on more than one page.

Setup

To display the Printer Setup dialog box, click the Setup button.


Print Selection dialog box

Use this dialog box to print the active file from the Code editor.

File To Print 	Lists the file that you are going to print. The file listed is the active page in the Code editor when you chose File|Print.

Print Selected Block	Sends only the selected block of text to the printer. This option is available only when you have text selected in the file. If this option is not checked, the entire file will print.

Header/Page Number	Includes the name of the file, current date, and page number at the top of each page.

Line Numbers	Places line numbers in the left margin.

Syntax Print	Uses bold, italic, and underline characters to indicate elements with syntax highlighting.

Use Color	Prints colors that match colors onscreen (requires a color printer).

Wrap Lines	Uses multiple lines to print characters beyond the page width. If not selected, code lines are truncated and characters beyond the page width do not print.

Left Margin	Specifies the number of character spaces used as a margin between the left edge of the page and the beginning of each line.

Setup 	Click the Setup to display the Printer Setup dialog box.


Printer Setup dialog box
Printer Setup

Changes printer options and selects a printer from a list. To display this dialog box, click Setup from the Print Selection dialog box.

For more information about setting printer options, see your Windows documentation.

Name	Select a printer from the list box.

Portrait	Prints text across the narrowest side of the paper (e.g. 8.5" x 11").

Landscape	Prints text along the widest side of the paper (such as, 11" x 8.5").

Paper Size	Specify the paper size.

Paper Source	Specify the paper tray or paper feeding method your printer uses.

Properties	Displays the Printer properties page for the currently selected printer.


File | Exit

Choose File|Exit to close the open project and then close Delphi.

If you exit Delphi before saving your changes, Delphi asks you if you want to save them.


Help menu

Use the Help menu to access the online Help system, which is displayed in a special Help window.

The Help system provides information on all aspects of the Delphi environment and libraries, the Object Pascal language, and so on.

Help options	Description

Contents	Opens the Help Topics diaolog. In the dialog, select the Contents tab to see a hierarchical list of topics; the Index tab to see topics listed alphabetically; or the Find tab for a full-text search of the Help system.

Keyword Search	Opens the Help Topics dialog with the Index tab selected. (See Contents above.)

What's New	Summarizes new features and enhancements in Delphi 3.0.

Getting Started	Provides information about key features of the Delphi user interface and fundamental programming tasks.

Using Object Pascal	Describes the syntax and semantics of the Object Pascal programming language.

Developing Applications	Provides in-depth information on intermediate and advanced programming tasks. Covers database applications, custom components, and Internet and intranet applications. Explains how to work with COM and ActiveX.

Object and Component Reference	A complete reference for the Visual Component Library, types, routines, variables, and constants.

Borland Home Page	Opens your Web browser and points it to Borland's World Wide Web site.

Delphi Home Page	A direct link to the Delphi home page on Borland's World Wide Web site.

Borland Programs and Services	A direct link to the Programs and Services page on Borland's World Wide Web site. Provides information about educational, consulting, technical-support, and other services offered by Borland.

About….	Shows copyright and version information for Delphi.


Form context menus for specific components
Context menus for specific components

When you right-click on a component in the Form Designer, you see a pop-up menu with shortcuts to frequently used design functions. These menus vary from component to component, but they all include the basic Form context menu options. The following additional options are available from the menus for specific components.

Action Editor (TWebDispatcher)

Assign Local Data (TClientDataSet)

Bands Editor (TCoolBar)

Columns Editor (TDBGrid, TListView)

Copy Object (TOleContainer)

Database Editor (TDataBase)

Decision Cube Editor (TDecisionCube)

Decision Query Editor (TDecisionQuery)

Define Parameters (TQuery)

Define Parameters (TStoredProc)

Delete Object (TOleContainer)

Display Sparse Rows/Columns (TDecisionSource)

Execute (TBatchMove)

Explore (TDatabase, TTable, TQuery, TStoredProc)

Fields Editor (TClientDataSet, TTable, TQuery, TStoredProc)

ImageList Editor (TImageList)

Input Mask Editor (TMaskEdit)

Insert Object (TOleContainer)

Items Editor (TListView)

Items Editor (TTreeView)

Load from File (TClientDataSet)

Masked Text Editor (TMaskEdit)

Menu Designer (TMainMenu, TPopupMenu)

New Button (TToolBar)

New Page (TPageControl, TTabSheet)

New Separator (TToolBar)

Next Frame (TAnimate)

Next Page (TPageControl, TTabSheet)

Object Properties (TOleContainer)

Panels Editor (TStatusBar)

Paste Special (TOleContainer)

Previous Frame (TAnimate)

Previous Page (TPageControl, TTabSheet)

Query Builder (TQuery)

Response Editor (TDataSetTableProducer, TQueryTableProducer)

Sections Editor (THeaderControl)

Subtotals on/off (TDecisionGrid)

UpdateSQL Editor (TUpdateSQL)

Your installation of Delphi may have third-party components with additional menu options.


Form context menu

See also

Use the Form context menu to manipulate components at design time.

To display the Form context menu:

Right-click anywhere in the client area of a form, or select the form and press Alt-F10.

Right-click on a specific component on the form, or select a component and press Alt-F10.

Select several components (by dragging the mouse across them or holding down the Shift key while clicking each one), then right-click or press Alt-F10.

The following commands always appear on the Form context menu:

Align To GridFormSMAligntoGrid

Bring To FrontFormSMBringtoFront

Send To BackFormSMSendtoBack

Revert To InheritedFormSMRevertToInherited

AlignFormSMAlign

SizeFormSMSize

ScaleFormSMScale

Tab OrderFormSMTabOrder

Creation OrderFormSMCreationOrder

Add To RepositoryAddingAFormToTheObjectRepository

View As TextFormSMViewAsText

Additional commands appear on the context menus for specific components.


See also

Context Menus for Specific Components

Form


Query Builder (Form context menu)

Choose Query Builder from the Form context menu when the Query component is selected, to open the Visual Query Builder. If a database is not already open, this command opens the Databases dialog box which enables you to select a database.


Execute (Form context menu)

See also

Choose Execute from the Form context menu when you have the BatchMove component selected, to perform at design time, the process specified in the Mode property.

The Mode property enables you to perform any of the following tasks:

Copy a dataset to a table.

Append a dataset to a table.

Update a table with data from a dataset.

Displaying data using data from a control.

To run this process at run time, you must call the Execute method for BatchMove.


See also

Copying data and table structures

TBatchMove component


Edit Report (Form context menu)

See also

Choose Edit Report from the Form context menu when you have the Report component selected, to load ReportSmith and open the report you specified using the ReportName and ReportDir properties.

If you have not previously specified a report, Delphi opens the ReportSmith Open Report dialog box which enables you to select a report.

You can also perform this command by double-clicking the Report component on the form.


See also

TReport component


Next Page (Form context menu)

See also

Choose Next Page from the Form context menu to change the ActivePage property of a TPageControl to the next TTabSheet.  Before you can change the active page, you must add the pages to the page control by choosing New Page from the Form context menu.


See also

Previous Page


Previous Page (Form context menu)

See also

Choose Previous Page from the Form context menu to change the ActivePage property of a TPageControl to the previous TTabSheet.  Before you can change the active page, you must add the pages to the page control by choosing New Page from the Form context menu.


See also

Next Page


Align To Grid (Form context menu)

Choose Align To Grid from the Form context menu to align the selected components to the closest grid point.

You can specify the size of the grid on the Preferences page of the Tools|Environment Options dialog box.

This command works the same as Edit|Align To Grid.


Bring To Front (Form context menu)

See also

Choose Bring To Front from the Form context menu to move a selected component in front of all other components on the form.

This is called changing the component's z-order.

This command works the same as Edit|Bring To Front.


Send To Back (Form context menu)

See also

Choose Send To Back from the Form context menu to move a selected component behind all other components on the form.

This is called changing the component's z-order.

This command works the same as Edit|Send To Back.


Revert To Inherited (Form context menu)

If a form inherits design features and properties from another form, you can choose Revert To Inherited from the Form context menu to restore the form to its original state. For example, if a form inherits a certain button placement from another form and you then move the button, Revert To Inherited returns the button to its orignal position.


Align (Form context menu)

See also

Choose Align from the Form context menu to open the Alignment dialog box.

Use this dialog box to line up selected components in relation to each other or to the form:

This command works the same as Edit|Align.


Size (Form context menu)

Choose Size from the Form context menu to open the Size dialog box.

Use this dialog box to resize multiple components to be exactly the same height or width.

This command works the same as Edit|Size.


Scale (Form context menu)

Choose Scale from the Form context menu to open the Scale dialog box.

Use this dialog box to proportionally resize the form and all of its components.

This command works the same as Edit|Scale.


Tab Order (Form context menu)

See also

Choose Tab Order from the Form context menu to open the Edit Tab Order dialog box.

Use this dialog box to modify the current tab order of the components on the active form or within the selected component if that component can contain other components.

This command works the same as Edit|Tab Order.


See also

Setting the tab order


Creation Order (Form context menu)

Choose Creation Order from the Form context menu to open the Creation Order dialog box.

Use this dialog box to specify the order in which your application will create nonvisual components.

This command works the same as Edit|Creation Order.


Add To Repository (Form context menu)

Choose Add To Repository from the Form context menu to open the Add To Repository dialog box. Use this command to easily add any form to the Object Repository.

Once you've designed a custom dialog box, you might want to reuse it in other projects. The best way to do this is to add the form to the Object Repository.

Saving a form as an object is similar to saving a copy of the form under a different name. When you save a form as a object, however, it then appears in the Object Repository.


View as Text (Forms context menu)
View As Text (Form context menu)

Use this command to view a text description of the form’s attributes.

Note:	This command changes to View As Form when you view the form as text.


View as Form
View As Form (Code Editor context menu)

Use this command to view a unit as a form. This option is available only for units that can produce a form when the form is not already visible in the IDE.

Note:	This command changes to View As Text when you view the unit as a form.


Select Icon dialog box

Use the Select Icon dialog box to choose a bitmap to represent your template in the New Items dialog box dialog box.

You can use a bitmap of any size, but it will be cropped to 60 x 40 pixels.

To open this dialog box:

Click the Browse button in the Add To Repository dialog box.


Project Manager context menu

See also

The Project Manager context menu contains commands that enable you to manage your project.

The commands on the Project Manager context menu are:

Save Project

Add Project To Repository

New Unit

New Form

Add File

Remove File

View Unit

View Form

View Project Source

Options

Update

To open the Project Manager context menu:

Choose View|Project Manager to open the Project Manager, then do one of the following:

Right-click anywhere on the Project Manager SpeedBar.

Press Alt+F10 when this is the active window.


See also

Project Manager


Save Project (Project Manager context menu)

Choose Save Project from the Project Manager context menu to store changes made to all files in the open project using each file's current name.

If you try to save a project that has an unsaved project file or unit file, Delphi opens the Save As dialog box, where you enter the new file name.

This command works the same as File|Save Project.


Add To Repository (Project Manager context menu)
Add Project To Repository (Project Manager context menu)

Choose Add Project To Repository from the Project Manager context menu to open the Add To Repository  dialog box.

You use the Add To Repository dialog box to add new forms to the Object Repository.

This command works the same as Project|Add To Repository.


Add to Repository dialog box (Project)
Add to Repository (Project)

Use this dialog box to save a project template to the Object Repository.

After saving an application as a template, use the Edit Object Info dialog box to edit the description, delete the template, or change the bitmap.

Dialog box options

Title	Enter the name of the template. The maximum length for a title is 40 characters.

Description	Enter a description of the template. The description appears under the template name on the Select Template dialog box. The maximum length for a description is 255 characters.

Page	From the drop down list box, choose the name of the page (probably Projects) you want the template to appear on.

Author	Enter text indentifying the author of the application. Author information only appears when you select the View Details option from the context menu.

Template bitmap	Click the Browse button to open the Select Icon dialog box.  You can use a bitmap of any size, but it will be cropped to 60 x 40 pixels.


New Unit (Project Manager context menu)

Choose New Unit from the Project Manager context menu to create a new unit and add it to the project. When you add a new unit, the Code Editor becomes the active window and the new unit is the active page.

The new unit is titled UnitXX.PAS. XX represents the unit number. For example, the first form is Unit1, the second Unit2, and so on.

You can change the unit name by saving the file with File|Save As, or by saving the entire project using Save Project from the Project Manager context menu.  Changing a unit name by any other means might cause an error.

This command works the same as the File|New Unit.


New Form (Project Manager context menu)

Choose New Form from the Project Manager context menu to create a blank form and a new unit and add them to the project.

The new form is titled FormXX and the new unit is UnitXX.PAS. (XX represents the form/unit number. For example, the first form is Form1, the second Form2, and so on.)

To change the name of the form,
Edit the Name property from the Object Inspector.

To change the unit name,
Save the file with File|Save As or save the entire project by using Save Project on the Project Manager context menu.

This command works the same as File|New Form.


Add File (Project Manager context menu)

See also

Choose Add File from the Project Manager context menu to open the Add To Project dialog box.

Use the Add To Project dialog box to add an existing unit and its associated form to the Delphi project. When you add a unit to a project, Delphi automatically adds that unit to the uses clause of the project file.

This command works the same as Project|Add to Project.


See also

Adding units and forms to a project

Remove file

Viewing and manipulating a project's contents


Remove File (Project Manager context menu)

See also

Choose Remove File from the Project Manager context menu to remove the module selected in the Project Manager from the uses clause of the current project file.

If you have modified the file you are removing during this editing session, Delphi prompts you to save your changes, just in case you want to use the form or unit in another project. If you have not modified the file, Delphi removes that file from the project without prompting you.

If a file has been modified during the current editing session, it is bold in the Project Manager.

Note:	Do not delete unit files by using other file management programs, or directly from the DOS prompt. Doing so causes errors.

This command works the same as File|Remove From Project.


See also

Add File

Removing forms and units from a project

Viewing and manipulating a project's contents


View Unit (Project Manager context menu)

See also

Choose View Unit from the Project Manager context menu to make the selected module in the Project Manager the active page of the Code Editor. It also makes the Code Editor the active window.

If the module you want to view is not currently open, Delphi opens it.

This command works the same as View|Units.


See also

Code Editor


View Form (Project Manager context menu)

See also

Choose View Form from the Project Manager context menu to make the form associated with the module selected in the Project Manager the active window.

If the form you want to view is not currently open, Delphi opens it.

This command works the same as View|Forms.


View Project (Project Manager context menu)
View Project Source (Project Manager context menu)

See also

Choose View Project Source from the Project Manager context menu to make the project file the active page in the Code Editor.

If the project file is not currently open, Delphi opens it.

This command works the same as View|Project Source.


Options (Project Manager context menu)

See also

Choose Options from the Project Manager context menu to open the Project|Options dialog box.

You can use this dialog box to set compiler directives.

This command works the same as the Project|Options.


See also

Compiler directives


Update (Project Manager context menu)

Choose Update from the Project Manager context menu to synchronize any changes you have made directly in the project (.DPR) file itself.

Note:	This command remains disabled (dimmed) unless you have manually edited the project file. Because Delphi maintains the project file for you automatically, manually modifying the .DPR file is not recommended.


SpeedBar context menu

See also

The SpeedBar context menu contains commands that enable you to edit or rearrange the buttons on the SpeedBar. You can also use the SpeedBar context menu to hide the SpeedBar.

The commands on the SpeedBar context menu are:

Show Hints

Hide

Help

Properties

To display the SpeedBar context menu:

Right-click anywhere on the SpeedBar.


See also

SpeedBar


Properties (SpeedBar context menu)

See also

Choose Properties from the SpeedBar context menu to open the SpeedBar Editor dialog box.

SpeedBar editor

Use this dialog box to add buttons that represent menu commands to the SpeedBar, remove buttons from the SpeedBar, or rearrange buttons on the SpeedBar.

Dialog box options

Categories	Select a menu whose commands you want to add as buttons to the SpeedBar. The commands on the selected menu appear in the Command list box.

Command	Drag and drop a command from this list box onto the SpeedBar. The Commands list box displays all the commands available on the menu selected in the Categories list box. The icon to the left of the menu command shows how the button will appear on the SpeedBar.

Reset	Choose Reset to reset the SpeedBar to the default configuration.

When the SpeedBar editor is open, you can delete or rearrange any of the buttons currently on the SpeedBar; however, none of the buttons on the SpeedBar is active.


See also

Configuring the SpeedBar

SpeedBar


Configuring the SpeedBar

See also

The Delphi SpeedBar is configurable. That is, you can do any of the following:

Add buttons to the SpeedBar

Remove buttons from the SpeedBar

Rearrange the buttons on the SpeedBar

Before you can configure the SpeedBar, you must open the SpeedBar editor by choosing Properties from the SpeedBar context menu.

To add buttons to the SpeedBar:

1.	Enlarge the SpeedBar area by dragging the separator line between the SpeedBar and Component palette to the right. When you are over the separator line, the cursor turns into a horizontally split cursor.

2.	Select a menu from the Categories list box. The menu commands on the selected menu are displayed in the Commands list box.

3.	Drag the menu command you want to add from the Commands list box and drop it on the SpeedBar in an open space.

Note:	You can have overlapping buttons on the SpeedBar, but this is not advisable.

To remove a button from the SpeedBar:

Drag the button off the SpeedBar. An open space appears where the button was.

To rearrange the buttons on the SpeedBar:

Drag and drop the button to a new position.


See also

SpeedBar


Hide (SpeedBar/Component Palette context menu)
Hide

Select Hide to close the display.

When closed, an item can be redisplayed from the View menu.

Hide is available from the following Speed Menus:

Alignment Palette context menu

Component Palette context menu

Object Inspector context menu

SpeedBar context menu


Show Hints

Choose Show Hints to toggle the display of Help Hints.  When this command is checked, Help Hints are enabled.

Show Hints is available from the following Speed Menus:

Alignment Palette context menu

Component Palette context menu

Object Inspector context menu

SpeedBar context menu


Help (SpeedBar context menu)
Help

Choose Help from the context menu to display Help for the item.

Help is available from the following:context menus :

Alignment Palette context menu

Component Palette context menu

Object Inspector context menu

SpeedBar context menu


Component Palette context menu

See also

The Component Palette context menu enables you to edit or rearrange the components on the Component palette. You can also use the Component Palette context menu to hide the Component palette.

Show Hints

Hide

Help

Properties

To display the Component Palette context menu:

Right-click anywhere on the SpeedBar.


See also

Component palette


Configure (Component Palette context menu)
Properties (Component Palette context menu)

See also

Choose Properties from the Component palette context menu to open the Palette page of the Tools | Environment Options dialog box.

Use this dialog box to rearrange the Components on the Component palette.


See also

Component palette

Using design-time packages

Adding components to Delphi


Alignment Palette context menu

See also

The Alignment Palette context menu contains the following commands:

Stay On Top

Show Hints

Hide

Help


See also

Alignment palette


Stay On Top (Alignment Palette context menu)
Stay On Top

Choose Stay On Top  to keep this item in front of all other Delphi windows and dialog boxes.


Object Inspector context menu

See also

The Object Inspector context menu provides you with commands for closing the Object Inspector, displaying Help, and for keeping the Object Inspector the topmost window.

The commands on the Object Inspector context menu are:

Revert To Inherited

Expand

Collapse

Stay On Top

Hide

Help


See also

Object Inspector


Stay On Top (Object Inspector context menu)

Choose Stay On Top from the Object Inspector context menu to keep the Object Inspector in front of all other Delphi windows and dialog boxes.


Expand (Object Inspector context menu);nested properties
Expand (Object Inspector context menu)

Choose Expand from the Object Inspector context menu to view the nested properties of the selected property.

Properties with nested properties show a plus (+) sign on their left side in the Object Inspector. You need to view these nested properties to set them.


Collapse (Object Inspector context menu)

Choose Collapse from the Object Inspector context menu to hide the nested properties of the selected property.

Properties with nested properties show a plus (+) sign on their left side in the Object Inspector. You need to view these nested properties to set them.


Breakpoint List context menu

See also

Use the Breakpoint List context menu to access commands that enable you to manipulate breakpoints.

The following commands appear on the Breakpoint List context menu:

Add	Opens the Edit Breakpoint dialog box, where you can create new breakpoints

Delete All	Removes all breakpoints

Disable All	Disables all enabled breakpoints

Enable All	Enables all disabled breakpoints

The following commands are available when you right-click on a defined breakpoint:

Enabled	Enables a disabled breakpoint

Delete	Removes a breakpoint

View Source	Locates a breakpoint in your source code quickly

Edit Source	Locates a breakpoint in your source code quickly and activates the Code Editor

Properties	Opens the Edit Breakpoint dialog box, where you can create new breakpoints

To display the Breakpoint List context menu:

Choose View|Breakpoints to display the Breakpoint List window, then do one of the following:

Right-click anywhere in the Breakpoint List window.

Press Alt+F10 when the Breakpoint List window is active.


See also

Using breakpoints


Edit Breakpoint (Breakpoint List context menu)
Properties  (Breakpoint List context menu)

See also	Breakpoint List context menu

Choose Properties from the Breakpoint List context menu to open the Edit Breakpoint dialog box, where you can create new breakpoints.

Alternate ways to perform this command are:

Choose Run|Add Breakpoint.

Choose Add Breakpoint from the Breakpoint List context menu.


See also

Using breakpoints

The Breakpoint List window

The Delphi Debugger


Add Breakpoint (Breakpoint List context menu)
Add (Breakpoint List context menu)

See also	Breakpoint List context menu

Choose Add from the Breakpoint List context menu to open the Edit Breakpoint dialog box, where you can create new breakpoints.

Alternate ways to perform this command are:

Choose Run|Add Breakpoint.

Right-click an existing breakpoint in the Breakpoint List window and choose Properties from the Breakpoint List context menu.


See also

Using breakpoints

The Breakpoint List window

The Delphi Debugger


Delete Breakpoint (Breakpoint List context menu)
Delete (Breakpoint List context menu)

See also	Breakpoint List context menu

Choose Delete from the Breakpoint List context menu to remove the selected breakpoint.

When you no longer need to examine the code at a breakpoint location, you can delete the breakpoint from the debugging session. This command is not reversible.


See also

Delete All Using breakpoints (Breakpoint List context menu)

Using breakpoints

The Breakpoint List window

The Delphi Debugger


Enable Breakpoint (Breakpoint List context menu)
Enabled (Breakpoint List context menu)

See also	Breakpoint List context menu

Choose Enabled from the Breakpoint List context menu to toggle a breakpoint on or off. The breakpoint is enabled when the option is checked..

Disabling a breakpoint hides the breakpoint from the current program run. When you disable a breakpoint, its settings remain defined, but the breakpoint does not cause your program to stop. When you set a breakpoint, it is enabled by default.

Disabling is useful when you temporarily do not need a breakpoint but want to preserve its settings.


See also

Enable All Using breakpoints (Breakpoint List context menu)

Disable Breakpoint (Breakpoint List context menu)

Disable All Using breakpoints (Breakpoint List context menu)

Using breakpoints

The Breakpoint List window

The Delphi Debugger


View Source (Breakpoint List context menu)

See also	Breakpoint List context menu

Choose View Source from the Breakpoint List context menu to locate a breakpoint in your source code quickly.

The View Source command scrolls the Code Editor to the location of the breakpoint that is selected in the Breakpoint List window.


See also

Edit Source (Breakpoint List context menu)

Using breakpoints

The Breakpoint List window

The Delphi Debugger


Edit Source (Breakpoint List context menu)

See also	Breakpoint List context menu

Choose Edit Source from the Breakpoint List context menu to locate a breakpoint in your source code quickly.

The Edit Source command scrolls the Code Editor to the location of the breakpoint that is selected in the Breakpoint List window, and activates the Code Editor.


See also

View Source (Breakpoint List context menu)

Using breakpoints

The Breakpoint List window

The Delphi Debugger


Disable All Breakpoints (Breakpoint List context menu)
Disable All (Breakpoint List context menu)

See also	Breakpoint List context menu

Choose Disable All from the Breakpoint List context menu to disable all enabled breakpoints.

Disabling a breakpoint hides the breakpoint from the current program run. When you disable a breakpoint, its settings remain defined, but the breakpoint does not cause your program to stop. When you set a breakpoint, it is enabled by default.

Disabling is useful when you temporarily do not need a breakpoint but want to preserve its settings.


See also

Disable Breakpoint (Breakpoint List context menu)

Enable Breakpoint (Breakpoint List context menu)

Enable All Using breakpoints (Breakpoint List context menu)

Using breakpoints

The Breakpoint List window

The Delphi Debugger


Enable All Breakpoints (Breakpoint List context menu)
Enable All (Breakpoint List context menu)

See also	Breakpoint List context menu

Choose Enable All to enable all disabled breakpoints.

Disabling a breakpoint hides the breakpoint from the current program run. When you disable a breakpoint, its settings remain defined, but the breakpoint does not cause your program to stop. When you set a breakpoint, it is enabled by default.

Disabling is useful when you temporarily do not need a breakpoint but want to preserve its settings.


See also

Enable Breakpoint (Breakpoint List context menu)

Disable Breakpoint (Breakpoint List context menu)

Disable All Using breakpoints (Breakpoint List context menu)

Using breakpoints

The Breakpoint List window

The Delphi Debugger


Delete All Breakpoints (Breakpoint List context menu)
Delete All (Breakpoint List context menu)

See also	Breakpoint List context menu

Choose Delete All from the Breakpoint List context menu to remove all breakpoints.

When you no longer need to examine the code at a breakpoint location, you can delete the breakpoint from the debugging session. This command is not reversible.


See also

Delete Breakpoint (Breakpoint List context menu)

Using breakpoints

The Breakpoint List window

The Delphi Debugger


Call Stack context menu

See also

Use the Call Stack context menu to access commands that enable you to examine previous function calls.

The commands on the Call Stack List context menu are:

View Source	Locates a function call in your source code quickly

Edit Source	Locates a function call in your source code quickly, and activates the Code Editor

To display the Call Stack context menu:

Choose View|Call Stack to display the Call Stack window, then do one of the following:

Right-click anywhere in the Call Stack window.

Press Alt+F10 when the Call Stack window is active.


See also

Call Stack


View Source (Call Stack context menu)

See also	Call Stack context menu

Choose View Source from the Call Stack context menu to locate a function call in your source code quickly.

The View Source command scrolls the Code Editor to the location of the function call that is selected in the Call Stack window.


See also

Edit Source (Call Stack context menu)

Viewing function calls

The Delphi Debugger


Edit Source (Call Stack context menu)

See also	Call Stack context menu

Choose Edit Source from the Call Stack context menu to locate a function call in your source code quickly.

The Edit Source command scrolls the Code Editor to the location of the function call that is selected in the Call Stack window, and makes the Code Editor active.


See also

View Source (Call Stack context menu)

Viewing function calls

The Delphi Debugger


Watch List context menu

See also

Use the Watch List context menu to access commands that enable you manipulate watch points.

The commands on the Watch List context menu are:

Edit Watch	Opens the Watch Properties dialog box, where you can create and modify watches

Add Watch	Opens the Watch Properties dialog box, where you can create and modify watches

Enable Watch	Enables a disabled watch expression

Disable Watch	Disables an enabled watch expression

Delete Watch	Removes a watch expression

Disable All Watching expressions	Enables all disabled watch expressions

Enable All Watching expressions	Disables all enabled watch expressions

Delete All Watching expressions	Removes all watch expressions

Stay on top	Keeps Watch List in front of all other Delphi windows and dialog boxes.

To display the Watch List context menu:

Choose View|Watches to display the Watch List window, then do one of the following:

Right-click anywhere in the Watch List window.

Press Alt+F10 when the Watch List window is active.


See also

WatchList


Edit Watch (Watch List context menu)

See also	Watch List context menu

Choose Edit Watch from the Watch List context menu to open the Watch Properties dialog box, where you can create and modify watches. After you create a watch, use the Watch List window to display and manage the current list of watches.

Alternate ways to perform this command are:

Choose Run|Add Watch.

Choose Add Watch At Cursor from the Code Editor context menu.

Right-click an existing watch in the Watch List window and choose Edit Watch from the Watch List context menu.


See also

Watching expressions

The Watch List window

The Delphi Debugger


Add Watch (Watch List context menu)

See also	Watch List context menu

Choose Add Watch from the Watch List context menu to open the Watch Properties dialog box, where you can create and modify watches. After you create a watch, use the Watch List window to display and manage the current list of watches.

Alternate ways to perform this command are:

Choose Run|Add Watch.

Choose Add Watch At Cursor from the Code Editor context menu.

Choose Add Watch from the Watch List context menu.


See also

Watching expressions

The Watch List window

The Delphi Debugger


Enable Watch (Watch List context menu)

See also	Watch List context menu

Choose Enable Watch from the Watch List context menu to enable a disabled watch expression.

Disabling a watch hides the watch from the current program run. When you disable a watch, its settings remain defined, but Delphi does not evaluate the watch.

Disabling watches improves performance of the debugger because it does not monitor the watch as you step through or run your program. When you set a watch, it is enabled by default.


See also

Enable All Watching expressions (Watch List context menu)

Disable Watch (Watch List context menu)

Disable All Watching expressions (Watch List context menu)

Watching expressions

The Watch List window

The Delphi Debugger


Disable Watch (Watch List context menu)

See also	Watch List context menu

Choose Disable Watch from the Watch List context menu to disable an enabled watch expression.

Disabling a watch hides the watch from the current program run. When you disable a watch, its settings remain defined, but Delphi does not evaluate the watch.

Disabling watches improves performance of the debugger because it does not monitor the watch as you step through or run your program. When you set a watch, it is enabled by default.


See also

Disable All Watching expressions (Watch List context menu)

Enable Watch (Watch List context menu)

Enable All Watching expressions (Watch List context menu)

Watching expressions

The Watch List window

The Delphi Debugger


Delete Watch (Watch List context menu)

See also	Watch List context menu

Choose Delete Watch from the Watch List context menu to remove a watch expression.

When you no longer need to examine the value of an expression, you can delete the watch from the debugging session. This command is not reversible.


See also

Delete All Watching expressions (Watch List context menu)

Watching expressions

The Watch List window

The Delphi Debugger


Enable All Watches (Watch List context menu)

See also	Watch List context menu

Choose Enable All Watches from the Watch List context menu to enable all disabled watch expressions.

Disabling a watch hides the watch from the current program run. When you disable a watch, its settings remain defined, but Delphi does not evaluate the watch.

Disabling watches improves performance of the debugger because it does not monitor the watch as you step through or run your program. When you set a watch, it is enabled by default.


See also

Enable Watch (Watch List context menu)

Disable Watch (Watch List context menu)

Disable All Watching expressions (Watch List context menu)

Watching expressions

The Watch List window

The Delphi Debugger


Disable All Watches (Watch List context menu)

See also	Watch List context menu

Choose Disable All Watches from the Watch List context menu to disable all enabled watch expressions.

Disabling a watch hides the watch from the current program run. When you disable a watch, its settings remain defined, but Delphi does not evaluate the watch.

Disabling watches improves performance of the debugger because it does not monitor the watch as you step through or run your program. When you set a watch, it is enabled by default.


See also

Disable Watch (Watch List context menu)

Enable Watch (Watch List context menu)

Enable All Watching expressions (Watch List context menu)

Watching expressions

The Watch List window

The Delphi Debugger


Delete All Watches (Watch List context menu)

See also	Watch List context menu

Choose Delete All Watches from the Watch List context menu to remove all watch expressions.

When you no longer need to examine the value of an expression, you can delete the watch from the debugging session. This command is not reversible.


See also

Delete Watch (Watch List context menu)

Watching expressions

The Watch List window

The Delphi Debugger


Assign Local Data dialog box

See Also

Copies the current set of records from a BDE dataset or client dataset to the selected client dataset. This is useful when populating client datasets for use as lookup tables, or when testing client datasets at design-time. Select the dataset you want to copy from the list of BDE datasets available to the current form, then click Ok.

To clear the records in a client dataset at design-time, make sure the dataset's Active property is set to True, then right-click the client dataset and choose Clear Data.


Ole Container context menu

See also

Right-click an Ole container in the form designer to display the Ole Container context menu.  In addition to the basic Form context menu options, additional items appear in this menu, depending on the state of the Ole container.  These include the following:

Menu Item	Meaning	When Present
Insert Object	Brings up the Insert Object dialog box to create a new OLE object or load an existing object from a file.	Always
Paste Special	Brings up the Paste Special dialog box to load an OLE object from the Clipboard	When the clipboard contains an OLE object.
Copy Object	Copies the currently loaded OLE object to the clipboard.	When an OLE object has been loaded by Create Object or Paste Special.
Delete Object	deletes the currently loaded OLE object from the Ole container, and frees all associated memory.	When an OLE object has been loaded by Create Object or Paste Special.
Object Properties	Displays the property sheet for the currently loaded OLE object.	When the OLE object that was loaded by Create Object or Paste Special includes a property sheet.
Other commands	Additional Verbs may be added by the OLE server application.  These appear before any other context menu commands.	When the OLE object that was loaded by Create Object or Paste Special includes additional verbs.


Project menu

See also

Use the Project menu commands to compile, or build your application. You must have a project open.

Add To Project	Add a file to a project

Remove From Project	Remove a file from a project

Import Type Library	Import a type library to a project.

Add To Repository	Add a project to the Object Repository

Compile	Compiles any source code that has changed since the last compile

Build All	Compiles everything in the project, regardless of whether source has changed

Syntax Check	Compiles your project but does not link it.

Information	Displays all the build information and build status for your project

Web Deployment Options	Make necessary settings to deploy a finished ActiveX control or ActiveForm to your web server.

Web Deploy	After setting the web deployment options and compiling your project, deploy your finished ActiveX control or ActiveForm.

Options	Displays the Project Options dialog box.


See also

Compiling, building and running projects


Projet|Add to Project
Project | Add to Project

See also

Choose Project|Add to Project to open the Add To Project dialog box.

Add To Project dialog box

Use the Add To Project dialog box to add an existing unit and its associated form to the Delphi project. When you add a unit to a project, Delphi automatically adds that unit to the uses clause of the project file.

Look In	Select the drive or directory in which to look for files to add to the project. The File window displays the subdirectories of the current drive or directory, as well as all the files that match the filter specified in the Files of Type combo box.

File Window	Displays the subdirectories of the current directory as well as the files in the current directory that match the current filter. Select a file to add to the project by clicking with the mouse or using the arrow keys. Right-click on the files to bring up a context menu.

File Name	Enter the name of the file you want to add to the project, or a filter expression to limit the files that appear in the file window.

Files of Type	See "Look In" above.

Up One Level	Click this button to move up a level from the current directory.

Create New Folder	Click this button to create a new subdirectory in the current directory.

List	Click this button to display the files as a list of files with associated icons.

Details	Click this button to display files in a tabular format that includes detailed information about each file's size, type, time stamp, and other attributes.


See also

Adding units and forms to a project

Viewing and manipulating a project's contents


Project | Remove From Project

See also

Choose File|Remove From Project remove a unit from the current project.

Remove From Project dialog box

Use this dialog box to select one or more units to remove from the current project. When you select a unit and click OK, Delphi removes the selected unit from the uses clause of the current project file but does not delete the file from your disk. Any references to the unit that you added must be removed manually. Use the control or shift key to select more than one unit.

If you have modified the file during this editing session, Delphi prompts you to save your changes, to preserve the unit in another project. If you have not modified the file, Delphi removes that file from the project without prompting you.

Note:	Do not delete unit files by using other file management programs, or directly from the DOS prompt. Doing so will cause errors.


See also

Removing forms and units from a project

Viewing and manipulating a project's contents


Import Type LIbrary
Project | Import Type Library

See also

The Import Type Library dialog displays the type libraries registered on your system so you can add them to your Delphi projects. You can generate Pascal declarations in a .PAS file that let you use these types as though they were native VCL objects. In effect, the type library is placed within a Delphi wrapper.

The top part of the dialog is a list of type libraries that are currently registered and thus available to be imported into Delphi. This lists lets you extract the Pascal declarations from an existing control or free-standing type library. You can also conveniently register a new type library from this dialog box so that it is available to be imported.

To add and register a new type library:

1.	Click Add. The Register OLE Control dialog box appears.

2.	In the Register OLE Control dialog box, navigate to the disk or network location of the library file you want to add.

3.	Select the new type library. It is automatically registered on your system for Delphi and immediately appears in the list of available libraries in the Import Type Library dialog.

Add button 	The Add button lets you locate a new type library and register it in the Windows Registry, so that it will appear in the list of registered objects available to be imported into Delphi.

Remove button 	To remove a registered type library, click the Remove button. The library is removed from the Windows Registry and from this list.

Class names 	Shows only the control classes in the selected library.

Unit dir name 	Shows the name of the directory that will contain the unit using this library. Only the path root is shown; no file name appears. The unit name is derived from the internal type library name. Click the Browse button to move up the directory tree.


See also

An overview of OLE technologies

Working with type libraries

Type Library editor

About type libraries

When to use a type library

Type Library deployment

Valid types

Creating a new type library

Adding an interface to the type library

Adding properties and methods to the type library

Adding a component object class to the type library

Adding an enumeration to the type library

Saving and registering type library information


Add To Repository
Project | Add To Repository

See also

Choose Project|Add To Repository to open the Add to Repository dialog box. Use this command to add a project to the Object Repository.

You can add your own projects and forms to those already available in the Object Repository. This is helpful in situations where you want to enforce a standard framework for programming projects throughout an organization.

Add to Repository dialog box

Use this dialog to save a project template to the Object Repository. After saving an application as a template, use the Edit Object Info dialog to edit the description, delete the template, or change the icon.

Title	Enter the name of the template. The maximum length for a title is 40 characters.

Description	Enter a description of the template. The description appears under the template name in the Select Template dialog. The maximum length for a description is 255 characters.

Page	From the drop-down list box, choose the name of the page (probably Projects) in the Repository that you want the template to appear on.

Author	Enter text identifying the author of the application. Author information appears only when you select View Details form the speed menu.

Template Icon	Click the Browse button to open the Select Icon dialog.


See also

Using Project Templates


Project | Compile

See also

Use Project|Compile to compile all files in the current project that have changed since the last build into a new executable file (.EXE).

This option is identical to Project|Build All, except that it builds only those files that have changed.

If you have selected Show Compiler Progress from the Preferences page of the Tools|Environment Options dialog box, the Compiling dialog box displays information about the compilation progress and results. When your application successfully compiles, choose OK to close the Compiling dialog box.

If the compiler encounters an error, Delphi reports that error on the status line of the Code Editor and places the cursor on the line of source code containing the error.

The compiler builds .EXE files according to the following rules:

The project (.DPR) file is always recompiled.

If the source code of a unit has changed since the last time the unit was compiled, the unit is compiled. When a unit is compiled, Delphi creates a file with a .DCU extension for that unit.

If Delphi cannot locate the source code for a unit, that unit is not recompiled.

If the interface part of a unit has changed, all the other units that depend on the changed unit are recompiled.

If a unit links in an .OBJ file (external routines), and the .OBJ file has changed, the unit is recompiled.

If a unit contains an Include file. and the Include file has changed, the unit is recompiled.

You may choose to compile only portions of your code if you use conditional directives and predefined symbols in your code.


Project | Build All

See also

Choose Project|Build All to rebuild all the components of your application regardless of whether they have changed.

This option is identical to Project|Compile, except that it rebuilds everything.

The Project|Build All command recompiles all files included in the project file.


Project | Syntax Check

Choose Project|Syntax Check to compile the modules of your project but not link them. This provides you with a means for checking your code for compile time errors.

If you do not have a project open when you choose this command, only the current module will compile.

Using Project|Syntax Check is faster than using Project|Compile because Delphi does not have to create the object code for the units.


Project | Information

Choose Project|Information to open the Information dialog box.

Information dialog box

Use this dialog box to view the program compilation information and compilation status for your project.

Program Information

The Program Information options provide you with information about your project.

Options	What it lists

Source Compiled	Total number of lines compiled

Code Size	Total size of the executable or DLL without debug information

Data Size	Memory needed to store the global variables

Initial Stack Size	Memory needed to store the local variables

File size	Size of final output file.

Status Information

The Status Information line displays whether or not your last compile succeeded or failed.


Project | Web Deployment Options

Choose Project| Web Deployment Options to configure a finished ActiveX control or ActiveForm for deployment to your web server.  You should first set web deployment options then compile the project. Then you may choose Project|Web Deploy to deploy the current open ActiveX project.

See Instructions on deploying ActiveX controls or ActiveForms on the Web

Default checkbox	If checked, saves the current settings from the dialog box’s Project, Packages, Additional Files, and Code Signing pages as the default options. To restore Delphi’s original default settings, delete or rename the DEFPROJ.DOF file.

The Web Deployment Options dialog box contains four tabbed pages of settings:

Project page	Specifies locations of files and the URL.  Also, configuration settings for CAB compression.

Packages Page	Specifies packages used by this project

Additional Files Page	Specifies other files associated with this project.

Code Signing Page	Lets end user view information on author of the control and whether the code has been modified since it was signed by the author.


Project Page

The options on the project page apply to the .OCX file or .CAB file containing the OCX and become the default options for any packages and additional files deployed with the project. You can set the file locations, URL, and other deployment options for the project.

Note:	These are paths only, not complete file names.

Directory/URL Options	Meaning	Example
Target Dir	Location as a path of the ActiveForm.OCX on the web server, either a standard or a UNC path	C:\INETPUB\wwwroot\
Target URL	Location as a URL of the ActiveForm.OCX on the web server	http://mymachine.borland.com/
HTML Dir	Location as a path of the HTML file that contains a reference to the ActiveForm, either a standard or a UNC path	C:\INETPUB\wwwroot\
You can deploy the .OCX and any packages and additional files as cabinet files.  A cabinet is a single file, usually with a .CAB file extension, that stores compressed files in a file library. Cabinet compression can decrease a file’s download time up to 70%. Each file that you deploy can be .CAB file compressed.

A compressed file can be spread over several cabinet files. During installation, the setup application decompresses the files stored in a cabinet and copies them to the user's system.

General Options	Meaning

Use CAB file compression	Compress the OCX, and required packages and additional files, unless specified otherwise

Include file version number	Include version number contained in Project|Options VersionInfo

Autoincrement release number	Automatically increment the projects release number contained in Project|Options VersionInfo

Code sign project	Code sign the .OCX or .CAB file containing the OCX, and unless specified otherwise, any DLLs or additional files to deploy.

Deploy required packages	If checked, any required packages will be deployed with the project.

Deploy additional files	If checked, any additional files specified on the Addition Files page will be deployed with the project.


Packages Page

When deploying your ActiveForm or ActiveX control, you have the option of specifying individual deployment options for each required package file used by the project as part of your deployment. Each of these packages can be code signed and put into .CAB files. Packages that ship with Delphi are already code signed with the Borland signature.

To modify the settings for a particular package, select the package in the “Packages used by project” list box, and modify the settings as needed.

CAB Options	Meaning

Compress in separate CAB	If checked, create a separate .CAB file for the package. This is the default.

Compress in project CAB	If checked, include the package in the project .CAB file

Output Options	Meaning

Use file VersionInfo	If checked, this option takes the version info from the resource in the package file and enters it into the.INF file for the project.

Code sign file	If checked, Code sign the package or .CAB file containing the package.

Directory/URL Options	Meaning

Target URL	Location as a Uniform Resource Locator (URL) of the package on your Web Server. If left blank, the Web browser will assume the file exists on the target machine. If the package is not found on the target machine, the download of the OCX will fail.

Target directory	Location as a path of the package on the Web server. This can be a standard path name or a UNC path.


Additional Files Page

Specifies other files associated with this project.

If the ActiveForm or ActiveX control is deployed with any packages and/or additional files, an .INF file is automatically created. This file specifies various files that must be downloaded and setup for the OCX to run. The syntax of the .INF file allows URLs pointing to files to download, and platform independence (by enumerating files for various platforms).

CAB Options	Meaning

Compress in separate CAB	If checked, create a separate .CAB file for the file. This is the default.

Compress in project CAB	If checked, include the file in the project .CAB file.

Output Options	Meaning

Use file VersionInfo	If checked, If the file contains VersonInfo resources, takes the version info from the resource in the file and enters it into the.INF file for the project.

Code sign file	If checked, code sign the file or .CAB file containing the file.

Directory/URL Options	Meaning

Target URL	Location as a Uniform Resource Locator (URL) of the file on your Web Server. If left blank, the Web browser will assume the file exists on the target machine. If the file is not found on the target machine, the download of the OCX will fail.

Target directory	Location as a path of the file on the Web server. This can be a standard path name or a UNC path. Leave blank to assume the file exists and ensure that it won’t be overwritten.


Code Signing Page

Code signing allows the user of the control to determine with certainty who wrote the control and that the code has not been modified since it was signed. Each file that you deploy can be code signed. Signing data does not alter it. Code signing generates a digital signature string that is added to the file.

The Code Signing page contains settings used when creating a digital signature. To create a digital signature, a hash value (also known as a message digest) is first created using the selected Cryptographic digest algorithm. This hash value is then signed, using the Private key. To obtain the private key and credentials certificate files, contact Microsoft.

Required information	Meaning

Credentials file	To obtain this certification file, contact Microsoft.

Private key	The private key is known only to its owner.  It is used to generate the digital signature. To obtain this file, contact Microsoft

Optional information	Meaning

Name of the application	Application name as you would like it to appear in the digital certificate displayed by the Web browser.

Application or company URL	An URL that provides a link to a web page for the product or company.

Cryptographic digest algorithm	Meaning

MD5	The MD5 hashing algorithm was developed by RSA Data Security, Inc and is commonly supported by most browsers. This algorithm generates 128-bit hash value. DEFAULT.

SHA 1	The SHA hashing algorithm was developed by the National Institute of Standards and Technology (NIST) and by the National Security Agency (NSA). This algorithm generates a 160-bit hash value.


Project | Web Deploy

Choose Project|Web Deploy to deploy a finished ActiveX control or ActiveForm to your web server. You would use this command only after setting the Web Deployment Options and then compiling your project.

The ActiveX library (.OCX) is placed in the target directory specified in the Web deployment options. An HTML file (.HTM) that contains a URL reference to the ActiveX library in the target directory is created in the HTML directory specified in the Web Deployment options. When this HTML file is viewed in a Web browser, your ActiveX control or ActiveForm runs as an embedded application within the browser.

See Instructions on deploying ActiveX controls or ActiveForms on the Web


Project | Options

Choose Project|Options to display the Project Options dialog box. Use the pages of this dialog box to specify form, application, compiler, and linker options for your project, and to manage project directories.

The pages of the Project Options dialog box are:

Forms

Application

Compiler

Linker

Directories/Conditionals

VersionInfo

Packages

To change pages in the dialog box:

Click the tab that represents the page you want to use.

To save the current project options as defaults:

Check Default and click OK.

Default	Check Default to save the current project options so that every new project you create will use those options.


Project|Options - Forms
Forms (Project | Options)

Use the Forms page of the Project Options dialog box to select the main form for your applications and to choose which of the available forms are automatically created and in which order.

Main Form	Displays the form users will see when they start your application. Use the drop-down list to select which form is the main form for the project. The main form is the first form listed in the Auto-Create Forms list box.

Auto-Create Forms	Lists those forms that are automatically added to the startup code of the project file and created at run time. You can rearrange the create order of forms by dragging and dropping forms to a new location. To select multiple forms, hold down the Shift key while selecting the form names.

Available Forms	Lists those forms that are used by your application but are not automatically created. If you want to create an instance of one of these forms, you must call the its Create method.

Default	Check Default to save the current project options so that every new project you create will use those options.

Arrow buttons	Use the arrow buttons to move one or more files or all the files from one list box to the other.

To move all the files from one list box into the other:

Click the double arrow buttons ( >> or << ).

Drag and drop the files from one list box into the other.

To move only the selected file or files from one list box into the other:

Click the single arrow buttons ( > or < ).

Drag and drop the file from one list box into the other.


Project|Options - Application
Application (Project | Options)

See also

Use the Application page of the Project Options dialog box to specify a title, a Help file, an icon, and an extension for your application.

Application Settings

Title	Specify a title to appear under the application's icon when the application is minimized. The character limit is 255 characters.

Help File	Specify the name of the Help file (.HLP) your application will use whenever your application calls Help. The Help file name is passed to the WinHelp function call.  If you are unsure of the Help file name, you can click the Browse button to display the Application Help File dialog box.

Icon	Displays the icon file (.ICO) that will represent the application in the Program Manager and when the application is minimized.  To change the icon, click Load Icon and Delphi displays the Application Icon dialog box, where you can select an icon.

Output Setting

Target file extension	Specify the file extension to be used for the target executable file. If the project is  an ActiveX application or DLL the standard file extension can be specified, such as .ocx for an ActiveX file.

Default	Check Default to save the current project options so that every new project you create will use those options.


See also

Specifying a form as the project main form

Application page (Project Options)

Application Icon dialog box


Application Icon dialog box

Use the Application Icon dialog box to select an icon that will represent your application in the Program Manager and when your application is minimized.

To display this dialog box:

Select Project | Options, select the Application page, click Load Icon.

File Name	Enter the name of the file you want to use, or enter or wildcards to use as filters in the Files window.

Files	Displays subdirectories of the current directory as well as all files in the current directory that match the filter in the File Name edit box or the file type in the Files Of Type combo box. Select an icon file for your application's icon using the mouse or arrow keys. Right-click on a file to bring up a context menu.

Files Of Type	Choose the type of file you want to use; the default file type is an icon (.ICO). All files in the current directory of the selected type appear in the Files list box.

Look In	Select the drive or directory in which to look for the icon file. The Files window displays the subdirectories of the current drive or directory as well as all files that match the current filter.


Application Help File dialog box

Use the Application Help File dialog box to select a Help file to use as the Help file for your application. The Help file you specify here is entered into the Help File edit box on the Applicaton page of the Project Options dialog box.

To display this dialog box:

Select Project | Options, select the Application page and click Browse

File Name	Enter the name of the file you want to use, or enter or wildcards to use as filters in the Files window.

Files Window	Displays all subdirectories in the current directory as well as files in the current directory that match the filter in the File Name edit box or the file type in the Files Of Type combo box. Select a Help file for your application using the mouse or arrow keys. Right-click on a file to bring up a context menu.

Files Of Type	Choose the type of file you want to use; the default file type is a Help file (.HLP). All files in the current directory of the selected type appear in the Files list box.

Look In	Select the drive or directory in which to look for the Help file. The Files window displays the subdirectories of the current drive or directory as well as all files that match the current filter.


Project|Options - Compiler
Compiler (Project | Options)

See also

Use the Compiler page of the Project Options dialog box to set options for how you want your program to compile. These options correspond to switch directives that you can also set directly in your program code.

Selecting an option is equivalent to setting the switch directive to its positive (+) state.

Code generation	Effect

Optimizations	Enables compiler optimizations.  Corresponds to {$O}.

Aligned record fields	Aligns elements in structures to 32-bit boundaries. Corresponds to {$A}.

Stack frames	Forces compiler to generate stack frames on all procedures and functions. Corresponds to {$W}.

Pentium-Safe FDIV	Generates code that detects a faulty floating-point division instruction. Corresponds to {$U}.

Runtime errors	Effect

Range Checking	Checks that array and string subscripts are within bounds. Corresponds to {$R}.

I/O Checking	Checks for I/O errors after every I/O call. Corresponds to {$I}.

Overflow Checking	Checks overflow for integer operations. Corresponds to {$Q}.

Syntax options	Effect

Strict Var-Strings 	Sets up string parameter error checking. Corresponds to {$V}. (If the Open parameters option is selected, this option is not applicable.)

Complete Boolean Eval 	Evaluates every piece of an expression in Boolean terms, regardless of whether the result of an operand evaluates as false. Corresponds to {$B}.

Extended Syntax 	Enables you to define a function call as a procedure and to ignore the function result. Also enables Pchar support. Corresponds to {$X}.

Typed @ Operator 	Controls the type of pointer returned by the @ operator. Corresponds to {$T}.

Open Parameters 	Enables open string parameters in procedure and function declarations. Corresponds to {$P}. Open parameters are generally safer, and more efficient.

Huge Strings	Enables new garbage collected strings.  The string keyword corresponds to the new AnsiString type with this option enabled.   Otherwise the string keyword corresponds to the ShortString type. Corresponds to {$H}.

Assignable Typed Constants	Enable this for backward compatability with Delphi 1.0.  When enabled, the compiler allows assignments to typed constants.  Corresponds to {$J}

Debugging	Effect

Debug Information 	Puts debug information into the unit (.DCU) file. Corresponds to {$D}.

Local Symbols 	Generates local symbol information. Corresponds to {$L}..

Symbol Information 	Generates symbol information. Corresponds to {$Y}.

Messages	Effect

Show Hints 	Causes the compiler to generate hint messages.

Show Warnings 	Causes the compiler to generate warning messages.

Assertions 	Generates code for assertions placed in code. Corresponds to {$C}..

Default check box

Check Default to save the current project options so that every new project you create will use those options.


See also

Compiler directives


Project|Options - Linker
Linker (Project | Options)

See also

Use the Linker page of the Project Options dialog box to specify how your program files are linked.

Map file

Select the type of map file produced, if any. The map file is placed in the Output Directory specified on the Directories/Conditionals page, and it has a .MAP extension.

Option	Effect

Off	Does not produce map file.

Segments	Linker produces a map file that includes a list of segments, the program start address, and any warning or error messages produced during the link.

Publics	Linker produces a map file that includes a list of segments, the program start address, any warning or error messages produced during the link, and a list of alphabetically sorted public symbols.

Detailed	Linker produces a map file that includes a list of segments, the program start address, any warning or error messages produced during the link, a list of alphabetically sorted public symbols, and an additional detailed segment map. The detailed segment map includes the segment address, length in bytes, segment name, group, and module information.

Linker output

Specify the output from the linker.

Option	Effect

Generate DCUs	Output standard Delphi DCU format files

Generate Object

Files	Output binary Object files (OBJ)

EXE and DLL Options

Check box	What it does

Generate Console Application	Causes linker to set a flag in the application’s .EXE file indicating a console mode application.

Include TD32 Debug Info	Places debug information in your program's executable file. This will make the resulting .EXE file larger, but it does not affect memory requirements or performance.

Use this option only if you are using an external debugger. Use of this option causes an increase in the length of time required to compile a project.

Memory sizes

Use these edit boxes to specify the minimum and maximum stack size and heap image base for the compiled executable. Memory-size settings can also be specified in your source code with the $M compiler directive.

Option	Specifies

Min Stack Size	Initial commited size of the stack..

Max Stack Size	Total reserved size of the stack.

Image Base	Specifies the preferred load address of the compiled image.  This value is typically only changed when compiling to a DLL.

Description

EXE Descriptor

This field can contain a string of up to 255 characters.  The string will be linked to $D and included in the executable file.  It is most often used to insert copyright information into the application. Copyright information can also be included as part of the VersionInfoMOptionsVersionInfo file.

Default

Check Default to save the current project options so that every new project you create will use those options.


See also

Compiler directives


Project|Options - Directories/Conditionals
Directories/Conditionals (Project | Options)

See also

Use the Directories/ Conditionals page of the Project Options dialog box to specify the location of files needed to compile, link, and distribute your program. Click the down arrow next to any edit box to choose from a list of previously entered directories or symbols.

Directories

Output Directory	Where the compiler should put the compiled units and the executable file.

Unit Output Directory	Specifies a separate directory to contain the .dcu files. Note, .DCP files can be relocated by setting the DCP output directory path on the library page of the Tools | Environment Options dialog box.

Search Path	The location of your source files.  Only those files on the compiler's search path or the library search path will be included in the build. If you try to build your project with a file not on the search path, you will receive a compiler error.  You must include the entire search path.

Guidelines for search paths

Use the following guidelines when entering directory names into the Search Path edit box:

Separate multiple directory path names with a semicolon (;).

Whitespace before and after the semicolon is allowed but not required.

You can use a maximum of 127 (note that this may change...) characters (including whitespace).

Relative and absolute path names are allowed, including path names relative to the logged position in drives other than the current one.

Debug source path	Search path for the debugger.  The debugger searches paths defined by the compiler by default. If the directory structure has changed since the last compile, a path can be entered here to include a file in the debugging session.

Conditional Defines	Symbols referenced in conditional compiler directives. You can separate multiple defines with semicolons.

Unit Aliases	Useful for backwards compatibility.  Specify alias names for units that may have changed names or were merged into a single unit.  The format is <oldunit>=<newunit>.  You can separate multiple aliases with semicolons.  The default value is WinTypes=Windows;WinProcs=Windows.Default

Default	Check to save the current project options so that every new project you create will use those options.


See also

Compiling, building, and running projects

Managing projects


VersionInfo (Project | Options)

Use this page to enable the version information option and to specify version information for the project.

Include version information in project	When enabled, version information can be entered and will be included in the compiled code. When version information is included, if a user right-clicks the program icon and selects properties version information is displayed.

Module Version Number	Major, Minor, Release, and Build each specify an unsigned  integer between 0 and 65,535.  The combined string defines a version number for the application.

Auto-increment build number	If enabled, the build number will be incremented each time the Project|Build All menu item is selected.  (Other compilations do not change the build number.)

Module Attributes

Module attributes are flags that can be included in the version information for informational use only.  If a project is compiled in debug mode, the debug flag will be included in the version information.  Each of the remaining flags can be selected as needed.

Attribute	Effect

Debug Build	Included to indicate that the project was compiled in debug mode.

Pre-Release	Include to indicate the version is not the commercially released product.

Special Build	Include to indicate that the version is a variation of the standard release.

Private Build	Include to indicate that the version was not built using standard release procedures.

DLL	Include to indicate that the project includes a dynamic-link library.

Key/Value

A default set of keys are included.

Key	Value indicates

CompanyName	The company that produced the file. Required.

FileDescription	File description. You can display this string in a list box during installation. Required

FileVersion	File version number. Required.

InternalName	File internal name. If file does not have internal name, use original filename, without extension. Required.

LegalCopyright	File copyright notices. Optional.

LegalTrademarks	Trademarks and registered trademarks that apply to file. Optional.

OriginalFilename	Original file name, not including path. Required.

ProductName	Name of product that file is distributed with. Required.

ProductVersion	Version of product that file is distributed with. Required.

Comments	Additional information for diagnostic purposes. Optional.

To edit key entries, select the key and re-enter the name.

To add key entries, right-click within the Key/Value table and select Add Key.

Language ID

The language ID indicates which Code Page the users system will require to run the application, that is, it indicates which language the application displays. The following languages are supported.

Constant	Language	Constant	Language

0x0401	Arabic	0x0415	Polish

0x0402	Bulgarian	0x0416	Brazilian Portuguese

0x0403	Catalan	0x0417	Rhaeto-Romanic

0x0404	Traditional Chinese	0x0418	Romanian

0x0405	Czech	0x0419	Russian

0x0406	Danish	0x041A	Croato-Serbian (Latin)

0x0407	German	0x041B	Slovak

0x0408	Greek	0x041C	Albanian

0x0409	U.S. English	0x041D	Swedish

0x040A	Castilian Spanish	0x041E	Thai

0x040B	Finnish	0x041F	Turkish

0x040C	French	0x0420	Urdu

0x040D	Hebrew	0x0421	Bahasa

0x040E	Hungarian	0x0804	Simplified Chinese

0x040F	Icelandic	0x0807	Swiss German

0x0410	Italian	0x0809	U.K. English

0x0411	Japanese	0x080A	Mexican Spanish

0x0412	Korean	0x080C	Belgian French

0x0413	Dutch	0x0C0C	Canadian French

0x0414	Norwegian - Bokml	0x100C	Swiss French

0x0810	Swiss Italian	0x0816	Portuguese

0x0813	Belgian Dutch	0x081A	Serbo-Croatian (Cyrillic)

0x0814	Norwegian - Nynorsk


Project|Options - Packages
Packages page (Project | Options, Component | Install packages)

See also

Use this page to specify the design-time packages installed in the IDE and the runtime packages required by your project.

Design Packages

A list of the design time packages available to the IDE. Items with a check mark are installed in the current project. When a package is installed, it may register components that appear on the Component palette, experts that appear on the menu bar and New Items (File | New) dialog, and property editors for custom components.

Exercise caution when uninstalling packages, whether by using the Remove button or by unchecking the package's check box. When a package is removed, any components registered by it become unavailable in the IDE. If a project contains forms that use unavailable components, you will not be able to load the forms; if this happens, reinstall the package and reload the unit.

The following buttons manipulate entries in the list.

Button	Description

Add	Installs a design time package. The package will be available in all projects.

Remove	Deletes the selected package. The package will become unavailable in all projects.

Edit	Opens the selected package in the Package editor if source code is available.

Components	Displays a list of the components included in the selected package.

As packages are installed and uninstalled, you may notice that the runtime package list is updated. Delphi automatically adds runtime packages that are required by installed design-time packages.

Runtime Packages

Check Build With Runtime Packages to use run-time packages when the executable file is created. Enter a list of packages to use as run-time packages, separated by semicolons (VCL30;VCLDB30;VCLDBX30), into the edit box or click Add and specify a package to add to the list.

When a package is used by a project, Delphi must find the package's .DCP file in order to compile. When Delphi cannot find the .DCP, it is often because the .DCP's directory is not included in the global Library Search Path. (To edit the Library Search Path, choose Tools | Environment Options and select the Library tab.)

Default

Check the "Default" check box in the lower left corner of the dialog to turn the current package configuration into the default configuration for all new projects.


See also

Compiling, building, and running projects

Managing projects


New Package

See Also

Use the New Package dialog box to specify the file name and description of a new package. Click OK to display the new package in the Package editor.

File Name

Enter the name of the package to create. You can include a directory path with the name; otherwise, the package will reside in the current directory. To open a file/directory selection dialog box, click Browse. If you type a file name directly in the New Package dialog box, the .DPK extension will be added automatically.

Package names must be unique within a project. If you name a package “STATS”, the Package editor generates a source file for it called “STATS.DPK”; the compiler generates an executable and a binary image called “STATS.DPL” and “STATS.DCP”, respectively. Use “STATS” to refer to the package in the requires clause of another package, or when using the package in an application.

Description

A brief description that appears in the Project Options dialog when the package is installed.


Into new package

See Also

Specify the file name and description of a new package. Click OK to display the new package in the Package editor. Delphi adds the component to the package and builds the package.

File Name

Enter the name of the package to create. You can include a directory path with the name; otherwise, the package will reside in the current directory. To open a file/directory selection dialog box, click Browse. If you type a file name directly in the New Package dialog box, the .DPK extension will be added automatically.

Package names must be unique within a project. If you name a package “STATS”, the Package editor generates a source file for it called “STATS.DPK”; the compiler generates an executable and a binary image called “STATS.DPL” and “STATS.DCP”, respectively. Use “STATS” to refer to the package in the requires clause of another package, or when using the package in an application.

Description

A brief description that appears when the package is installed.


Into existing package

See Also

Specify the file name of the package to install into, then click OK to display the package in the Package editor. Delphi adds the component to the package and rebuilds the package if necessary.

File Name	Use the drop-down list to select the name of an installed package, or enter the name of another existing package.

Description	A brief description of the selected package.


Add Design Package

See Also

Use the Add Design Package dialog box to specify the name of a design time package to add to the Design packages list. This is a standard Windows Open dialog.


Add Runtime Package

See Also

Use the Add Runtime Package dialog box to specify the name of a runtime package to add to the Design packages list.

Package Name	Type the name of the package to add to the Runtime Packages list,or click the Browse button to search for the package using the Package File Name dialog. If the package is in the Search Path, a full path name is not required. (If the package directory is not in the Search Path, it will be added at the end.)

Search Path	If you haven't included a full directory path in the Package Name edit box (see above), make sure the directory where your package resides is in this list. If you add a directory in the Search Path edit box, you will be changing the global Library Search Path for Delphi.


See Also

About packages


Close this dialog box and open package?

When editing a package selected in the Design packages list, Delphi closes the Project Options dialog box and displays the selected package in the package editor. Click Yes to edit the package or No to return to the Project Options dialog box.


Another file with the same base name <file> is already on the search path

This error message appears when a file is found on the search path but not at the location specified by the user when creating a new component, installing a component, importing an ActiveX control, or adding a required package to a package. Because the Library Search Path is used to locate files when building a package, this conflict prevents the compiler from finding the intended file.

The problem can be solved by modifying the Search Path so that the directory of the intended file precedes the directory of the conflicting file.


Package package will be built then installed. Continue?

This message appears when adding a component or ActiveX control to a package. Click Yes to rebuild the package to reflect the new component or control.


Package(s) <package list> will also be uninstalled because they require package <package name>. Continue uninstall?

Appears when installed packages rely on packages that you’re trying to uninstall. Click Yes to uninstall the dependent packages.


Components

See Also

Lists components in the selected package, along with the icons for those components that appear on the Component palette if the package is installed.


Package editor

See Also

The Package editor lists the units in a package, and the other packages it requires. You can save your changes with File|Save or Save As.

Contains page	The Contains page lists the units included in the package. To add a unit to the package, click the Add button. To edit a unit's source code, double-click it.

Requires page	The Requires page lists the other packages required by the current package. To add a package, click Add.  To display a package in its own Package editor, double-click it.

Package editor SpeedBar

Button	Description

Compile	Compiles the current package. If changes to the package are required, a dialog box appears that lists the changes that will be made to the package before it is compiled.

Add	Adds an item (see the Contains page and Requires page for details).

Remove	Removes the selected item from the package.

Options	Displays the Package Options Dialog Box.

Install	Installs the current package as a design time package. If changes to the package are required, a dialog box appears that lists the changes that will be made to the package before it is compiled.

Update	Updates the Package editor to reflect changes made in the .DPK file. This button is enabled when the .DPK file has been modified using the Code editor; as an additional indication that the .DPK file has been modified, entries in the Package editor appear grayed.

Package editor context menu

To display the Package editor context menu, right-click an item within either Package editor page.

Command	Description

Save Package	Saves the current package

Compile	Equivalent to Compile button on SpeedBar

Build All	Builds all items in the package, whether or not they have changed

Add Unit	Equivalent to Add button on SpeedBar

Remove Unit	Equivalent to Remove button on SpeedBar

Add Required Package	Equivalent to Add button on SpeedBar

Remove Required Package	Equivalent to Remove button on SpeedBar

View Unit	Lets you view the selected unit’s source code

View Required Package	Lets you view the selected package in a new Package editor

View Package Source	Lets you view the package source code (.DPK file)

(View | Toggle Form/Unit can also be used to switch between the Package and Code editors.)

Options	Equivalent to Options button on SpeedBar

Install	Equivalent to Install button on SpeedBar

Update	Equivalent to Update button on SpeedBar


Add Unit

Enter the name of the unit file to add in Unit File Name (or select it using Browse). If you enter a name, Delphi searches the paths specified in Search path.

Unit File Name	The name of the unit to add. If the unit is in the Search Path, a full path name is not required. If the unit directory is not in the Search Path, it will be added to the end.

Search Path	If you haven't included a full directory path in the Unit File Name edit box (see above), make sure the directory where the unit resides is in this list. If you add a directory in the Search Path edit box, you will be changing the global Library Search Path for Delphi.


Add Package

Enter the name of the package to add in Package name (or select it using Browse). If you enter a name, Delphi searches the paths specified in Search path.

Package Name	Enter the name of the package to add. If the package is in the Search Path, a full path name is not required. (If the package directory is not in the Search Path, it will be added to the end.)

Search Path	If you haven't included a full directory path in the Package Name edit box (see above), make sure the directory where your package resides is in this list. If you add a directory in the Search Path edit box, you will be changing the global Library Search Path for Delphi.

When a package is required by another package, Delphi must find the package's .DCP file in order to compile.


See Also

Package Collection editor

About packages


Package Options dialog box

See Also

The Package Options dialog box lets you specify compiler options for the current package. The pages of the Project Options dialog box are:

Description

Compiler

Linker

Conditionals

VersionInfo

Global

To change pages in the dialog box:

Click the tab at the top of the dialog box that represents the page you want to use.


See Also

About packages


Description page
Description page (Package Options dialog box)

See Also

The Description page lets you specify a description for the package, the uses of the package, and how the package is built.

Description	A brief description that appears when the package is installed.

Usage options	Check Design Package if you want the package to be installable on the Component palette.

Check Runtime Package if you want the package to be deployable with an application.

Check both Design Package and Runtime Package if you want the package to be both installable and deployable.

If neither Design Package nor Runtime Package is checked, the package cannot be installed on the Component palette or deployed with applications. Use this option for packages that exist only to be referenced (required) by other (design-time) packages.

If your package uses custom property editors, it's a good idea to compile separate design-time and runtime versions of it. The deployed runtime package will be smaller than the design-time version, since it won't contain code for the property editors.

Build control	If the package is low-level and does not change often, click Explicit Rebuild.  It is built only when you display it in the Package editor and click Build. For automatic compilation, click Rebuild As Needed.


See Also

Package Options dialog box

About packages


Compiler page
Compiler page (Package Options dialog box)

See Also

Use the Compiler page to set options for how you want your package to compile. These options correspond to switch directives that you can also set directly in your package code.

Selecting an option is equivalent to setting the switch directive to its positive (+) state.

Code generation	Effect

Optimizations	Enables compiler optimizations.  Corresponds to {$O}.

Aligned record fields	Aligns elements in structures to 32-bit boundaries. Corresponds to {$A}.

Pentium-Safe FDIV	Generates code that detects a faulty floating-point division instruction. Corresponds to {$U}.

Stack frames	Forces compiler to generate stack frames on all procedures and functions. Corresponds to {$W}.

Runtime errors	Effect

Range Checking	Checks that array and string subscripts are within bounds. Corresponds to {$R}.

I/O Checking	Checks for I/O errors after every I/O call. Corresponds to {$I}.

Overflow Checking	Checks overflow for integer operations. Corresponds to {$Q}.

Syntax options	Effect

Strict Var-Strings 	Sets up string parameter error checking. Corresponds to {$V}. (If the Open parameters option is selected, this option is not applicable.)

Complete Boolean Eval 	Evaluates every piece of an expression in Boolean terms, regardless of whether the result of an operand evaluates as false. Corresponds to {$B}

Extended Syntax 	Enables you to define a function call as a procedure and to ignore the function result. Also enables Pchar support. Corresponds to {$X}.

Typed @ Operator 	Controls the type of pointer returned by the @ operator. Corresponds to {$T}

Open Parameters 	Enables open string parameters in procedure and function declarations. Corresponds to {$P}. Open parameters are generally safer, and more efficient.

Huge Strings	Enables garbage collected strings.  The string keyword corresponds to the new AnsiString type with this option enabled.   Otherwise the string keyword corresponds to the ShortString type. Corresponds to {$H}.

Assignable Typed Constants	Enable this for backward compatability with Delphi 1.0.  When enabled, the compiler allows assignments to typed constants.  Corresponds to {$J}.

Debugging	Effect

Debug Information	Puts debug information into the unit (.DCU) file. Corresponds to {$D}.

Local Symbols	Generates local symbol information. Corresponds to {$L}..

Symbol Information	Generates symbol cross-reference information. Corresponds to {$Y}.


See Also

Package Options dialog box

About packages


Linker page
Linker page (Package Options dialog box)

See Also

Use the Linker page of the Package Options dialog box to specify how your program files are linked.

Memory sizes

Use this edit box to specify the heap image base for the compiled executable. You can also specify memory-size settings in your source code by using the $M compiler directive.

Option	Specifies

Image Base	Specifies the preferred load address of the compiled image.


See Also

Package Options dialog box

About packages


Conditionals page
Conditionals page (Package Options dialog box)

See Also

Use the Conditionals page to specify compiler directives. Click the down arrow next to the edit box to choose from a list of previously entered symbols.

Conditional Defines	Symbols referenced in conditional compiler directives. You can separate multiple defines with semicolons.


See Also

Package Options dialog box

About packages


VersionInfo page
VersionInfo page (Package Options dialog box)

See Also

Use this page to enable the version information option and to specify version information for the package.

Module Version Number

Major, Minor, Release, and Build each specify an unsigned integer between 0 and 65,535.  The combined string defines a version number for the application.

Module Attributes

Module attributes are flags that can be included in the version information for informational use only.  If a package is compiled in debug mode, the debug flag will be included in the version information.  You can select each of the remaining flags as needed.

Attribute	Effect

Debug Build	Included to indicate that the package was compiled in debug mode.

Pre-Release	Include to indicate the version is not the commercially released product.

Special Build	Include tp indicate that the version is a variation of the standard release.

Private Build	Include to indicate that the version was not built using standard release procedures.

DLL	Include to indicate that the package includes a dynamic-link library.

Key/Value

A default set of keys are included.

Key	Value indicates

CompanyName	The company that produced the file. Required.

FileDescription	File description. You can display this string in a list box during

installation. Required

FileVersion	File version number. Required.

InternalName	File internal name. If file does not have internal name, use original

filename, without extension. Required.

LegalCopyright	File copyright notices. Optional.

LegalTrademarks	Trademarks and registered trademarks that apply to file. Optional.

OriginalFilename	Original file name, not including path. Required.

ProductName	Name of product that file is distributed with. Required.

ProductVersion	Version of product that file is distributed with. Required.

Comments	Additional information for diagnostic purposes. Optional.

Key entries can be edited by selecting the key and re-entering the name. Key entries can be added by right-clicking within the Key/Value table and selecting Add Key.

Language ID

The language ID indicates which Code Page the users system will require to run the application, that is, it indicates which language the application displays. The following languages are supported.

Constant	Language	Constant	Language

0x0401	Arabic	0x0415	Polish

0x0402	Bulgarian	0x0416	Brazilian Portuguese

0x0403	Catalan	0x0417	Rhaeto-Romanic

0x0404	Traditional Chinese	0x0418	Romanian

0x0405	Czech	0x0419	Russian

0x0406	Danish	0x041A	Croato-Serbian (Latin)

0x0407	German	0x041B	Slovak

0x0408	Greek	0x041C	Albanian

0x0409	U.S. English	0x041D	Swedish

0x040A	Castilian Spanish	0x041E	Thai

0x040B	Finnish	0x041F	Turkish

0x040C	French	0x0420	Urdu

0x040D	Hebrew	0x0421	Bahasa

0x040E	Hungarian	0x0804	Simplified Chinese

0x040F	Icelandic	0x0807	Swiss German

0x0410	Italian	0x0809	U.K. English

0x0411	Japanese	0x080A	Mexican Spanish

0x0412	Korean	0x080C	Belgian French

0x0413	Dutch	0x0C0C	Canadian French

0x0414	Norwegian - Bokml	0x100C	Swiss French

0x0810	Swiss Italian	0x0816	Portuguese

0x0813	Belgian Dutch	0x081A	Serbo-Croatian (Cyrillic)

0x0814	Norwegian - Nynorsk


See Also

Package Options dialog box

About packages


Global page
Global page (Package Options dialog box), Library page (Tools | Environment Options)

See Also

Use this page to specify directories, compiler, and linker options for all packages. Click the down arrow next to the edit boxes to choose from a list of previously entered symbols.

Map file

Select the type of map file produced, if any. The map file is placed in the directory specfied by DCP Directory, and it has a .MAP extension.

Option	Effect

Off	Does not produce map file.

Segments	Linker produces a map file that includes a list of segments, the program start address, and any warning or error messages produced during the link.

Publics	Linker produces a map file that includes a list of segments, the program start address, any warning or error messages produced during the link, and a list of alphabetically sorted public symbols.

Detailed	Linker produces a map file that includes a list of segments, the program start address, any warning or error messages produced during the link, a list of alphabetically sorted public symbols, and an additional detailed segment map. The detailed segment map includes the segment address, length in bytes, segment name, group, and module information.

Messages	Effect

Show Hints 	Causes the compiler to generate hint messages.

Show Warnings 	Causes the compiler to generate warning messages.

Options	Effect

Compile with debug info	Check to compile the package file with debug information. This will make the resulting file larger, but it does not affect memory requirements or performance.

When you compile a package using debug information, you can use the integrated debugger or Turbo Debugger for Windows to debug the library file.

Directories

DPL output directory	Where the compiler should put the compiled package (.dpl) file.

DCP output directory	Specifies a separate directory to contain the .dcp files.

Library path	Specify search paths where compiler can find the source files for the package. The compiler can find only those files listed in Library Path. If you try to build your package with a file not on the library path, you will receive a compiler error.

Aliases

Unit Aliases	Specify alias names for units that may have changed names or were merged into a single unit. The format is <oldunit>=<newunit>. You can separate multiple aliases with semicolons.


See Also

Package Options dialog box

About packages

About package collections


Change Package dialog box
Change Package dialog box (Package editor)

See Also

The Change dialog appears when the Package editor tries to compile a package and detects that the package cannot be built or is incompatible with another package currently loaded by the IDE. This occurs because the package uses a unit or units that are found in another package; select View Details to see which units are causing the problem.

The solution is to add the other package to the "requires" clause of the package you are editing, or simply not use the problem units at all. Click OK to let the Package editor make the proposed changes and continue compiling. Click Cancel to leave the package as it is.


See Also

About packages

Compiling packages


duplicate file name error
Duplicate file name error (Package editor)

See Also

This error message appears when a file in the directory of the package has the same name as a file in a different location specified by the user. Because the directory of the package .DPK file is searched first when building a package, the naming conflict prevents the compiler from finding the intended file.

This problem can arise when creating a component, installing a component, importing an ActiveX control, or adding a contained unit or required package to a package.

The problem can be solved by saving the package .DPK file to a different directory.


See Also

About packages

Compiling packages


Package Collection editor

See Also

The Package Collection editor lets you view and edit the packages and other files associated with a package collection.

File menu

Command	Description

New	Creates a new package collection

Open	Opens a package collection

Save	Saves the current package collection

Save As	Lets you save the current package collection under a different name

Exit	Closes the Package collection editor

Edit menu

Command	Description

Add Package	Lets you add a package to the collection

Remove Package	Removes the selected package from the collection

Add File Group	Lets you specify a set of files associated with the selected package

Remove File Group	Removes the selected file group from the collection

Compile Collection	Compiles the package collection

Collection tree

The collection tree is a hierarchical display of the packages and file groups in a collection.  The controls to the right of the Collection tree change to reflect options for the current selection.

Collection Control	Description

Author/Vendor Name	Optional information about your package collection that appears in the Installation dialog box when users install packages.

Directory List	Lists the default directories where you want the files in your package collection to be installed. Use the Add, Edit, and Delete buttons to edit this list. For example, suppose you want all source code files to be copied to the same directory. In this case, you might enter “Source” as a Directory Name with “C:\MyPackage\Source” as the Suggested Path. The Installation dialog box will display “C:\MyPackage\Source” as the suggested path for the directory.

Package Control	Description

Required Executables Directory	Specifies the directory where you want the .DPL files for packages listed in the requires clause to be installed. (The drop-down list includes the directories you entered under Directory List.) The Package Collection Editor searches for these files using Delphi’s global Library Path and lists them under Required Executable Files.

Required Executables Files	See the description for Required Executables Directory.

Required Libraries Directory	Specifies the directory where you want the .DCP files for packages listed in the requires clause to be installed. (The drop-down list includes the directories you entered under Directory List.) The Package Collection Editor searches for these files using Delphi’s global Library Path and lists them under Required Library Files.

Required Libraries Files	See the description for Required Libraries Directory.

File Group Control	Description

Install Directory	Specifies the directory where you want files in the selected group to be installed. The drop-down list includes the directories you entered under Directory List.

Optional Group	Specifies whether the installation of files in the selected group is optional.

Included Files	Lists the files you want to include in the selected group. Use the Add, Delete, and Auto buttons to edit the list. The Auto button lets you select all files with specified extensions that are listed in the contains clause of the package; the Package Collection editor uses Delphi’s global Library Path to search for these files.

Package Collection editor speedbar

Button	Description

Add Package	Lets you specify a package to add to the collection

Removed Package	Removes the selected package and any file groups associated with it

Add File Group	Associates a file group with the selected package

Remove File Group	Removes the selected file group

Compile	Compiles the collection


See Also

Package editor

About packages

Design-time packages

Compiling packages

Deploying packages


About packages

See Also

A package is a special dynamic-link library used by Delphi applications, the Delphi IDE, or both. Runtime packages provide functionality when a user runs an application. Design-time packages are used to install components in Delphi’s IDE and to create special property editors for custom components. A single package can function at both design time and runtime, and design-time packages frequently work by calling runtime packages. To distinguish them from other DLLs, package libraries are stored in files that end with the .DPL (Delphi package library) extension.

Like other runtime libraries, packages contain code that can be shared among applications. For example, most of Delphi’s commonly used components reside in a package called VCL30. Each time you create an application, you can specify that it uses VCL30. When you compile an application created this way, the application’s executable image contains only the code and data unique to it; the common code is in VCL30.DPL. A computer with several package-enabled applications installed on it needs only a single copy of VCL30.DPL, which is shared by all the applications and the Delphi IDE itself.

Delphi ships with several pre-compiled runtime packages, including VCL30, that encapsulate new and previously-existing components. Delphi also uses special design-time packages that facilitate manipulation of components in the IDE.

You can build applications with or without packages. However, if you want to add custom components to the IDE, you must install them as design-time packages.

You can create your own runtime packages to share among applications. If you write Delphi components, you'll compile them into design-time packages before installing them.

Why use packages?

Design-time packages, a standard feature of Delphi, simplify the tasks of distributing and installing custom components. Runtime packages are optional, but offer several advantages over conventional programming. By compiling reused code into a runtime library, you can share it among applications. For example, all of your applications—including the Delphi IDE itself—can access standard components through packages. Since the applications don’t have separate copies of the component library bound into their executables, the executables are much smaller. The result is a dramatic savings in both system resources and hard disk storage. Moreover, packages allow faster compilation because only code unique to the application is compiled with each build.


See Also

About package collections

Using packages in an application

Runtime packages

Design-time packages

Installing component packages

Creating and editing packages

Compiling packages

Deploying packages


Runtime packages

See Also

Runtime packages are deployed with Delphi applications. They provide functionality when a user runs the application.

To run an application that uses packages, a computer must have both the application’s .EXE file and all the packages (.DPL files) that the application uses. When you deploy an application, you must make sure that users have correct versions of any required DPLs.

Deciding which runtime packages to use

Delphi ships with several precompiled runtime packages, including VCL30, which supply basic Object Pascal and VCL component support. In previous versions of Delphi the functionality provided by these packages was obtained by binding a complete copy of the component library into each application.

The VCL30 package contains the most commonly used components, system functions, and Windows interface elements. It does not include database or Windows 3.1 components, which are available in separate packages. The following table lists the runtime packages shipped with Delphi and the units they contain.

Package	Units

VCL30.DPL	Ax, Buttons, Classes, Clipbrd, Comctrls, Commctrl, Commdlg, Comobj, Comstrs, Consts, Controls, Ddeml, Dialogs, Dlgs, Dsgnintf, Dsgnwnds, Editintf, Exptintf, Extctrls, Extdlgs, Fileintf, Forms, Graphics, Grids, Imm, IniFiles, Isapi, Isapi2, Istreams, Libhelp, Libintf, Lzexpand, Mapi, Mask, Math, Menu, Messages, Mmsystem, Nsapi, Ole2I, Oleconst, Olectnrs, Olectrls, Oledlg, Penwin, Printers, Proxies, Registry, Regstr, Richedit, Shellapi, Shlobj, Stdctrls, Stdvcl, Sysutils, Tlhelp32, Toolintf, Toolwin, Typinfo, Vclcom, Virtintf, Windows, Wininet, Winsock, Winspool, Winsvc

VCLX30.DPL	Checklst, Colorgrd, Ddeman, Filectrl, Mplayer, Outline, Tabnotbk, Tabs

VCLDB30.DPL	Bde, Bdeconst, Bdeprov, Db, Dbcgrids, Dbclient, Dbcommon, Dbconsts, Dbctrls, Dbgrids, Dbinpreq, Dblogdlg, Dbpwdlg, Dbtables, Dsintf, Provider, SMintf

VCLDBX30.DPL	Dblookup, Report

DSS30.DPL	Mxarrays, Mxbutton, Mxcommon, Mxconsts, Mxdb, Mxdcube, Mxdssqry, Mxgraph, Mxgrid, Mxpivsrc, Mxqedcom, Mxqparse, Mxqryedt, Mxstore, Mxtables, Mxqvb

INET30.DPL	Cgiapp, Copyprsr, Httpapp, Isapiapp, Masks, Scktcomp, Syncobjs, Webconst

INETDB30.DPL	Dbweb

QRPT30.DPL	Qr2const, Qrabout, Qralias, Qrctrls, Qrdatasu, Qrexpbld, Qrextra, Qrprev, Qrprgres, Qrprntr, Qrqred32, Quickrpt

TEE30.DPL	Arrowcha, Bubblech, Chart, Ganttch, Series, Teeconst, TeeFunci, TeEngine, Teeprocs, Teeshape

TEEDB30.DPL	Dbchart, Qrtee

IBEVNT30.DPL	Ibctrls, Ibevnts, Ibproc32, Ibreg

Suppose you want to create a client/server database application that uses packages. In this case, you need at least two runtime packages: VCL30 and VCLDB30. If you want to use Outline components in your application, then you also need VCLX30. To use these packages, choose Project|Options, select the Packages tab, and enter the following list in the Runtime Packages edit box.

VCL30;VCLDB30;VCLX30

Actually, you don’t have to include VCL30 in this list, because VCL30 is referenced in the requires clause of VCLDB30. Your application will compile just the same whether or not VCL30 is included in the list.

Custom packages

A custom package is either a DPL you code and compile yourself, or a precompiled package from a third-party vendor. To use a custom runtime package with an application, choose Project|Options and add the name of the package to the Runtime Packages edit box on the Packages page. For example, suppose you created a statistical package called STATS.DPL. To use it in an application, the line you enter in the Runtime Packages edit box might look like this:

VCL30;VCLDB30;STATS

If you create your own packages, you can add them to the list as needed.


See Also

About packages

Using packages in an application

Deploying packages


Using packages in an application

See Also

To use packages in an application:

1.	Load or create a project in the IDE.

2.	Choose Project|Options.

3.	Choose the Packages tab.

4.	Select the Build with Runtime Packages check box, and enter one or more package names in the edit box underneath. (Runtime packages associated with installed design-time packages are already on the list.) To add a package to an existing list, click the Add button and enter the name of the new package in the Add Package dialog box. To browse from a list of available packages, click the Add button, then click the Browse button in the Add Package dialog box box.

If you use the Search Path edit box in the Add Package dialog box, you will be changing the global Library Path for Delphi.

You do not need to include file extensions with package names. If you type directly into the Runtime Packages edit box, be sure to separate multiple names with semicolons. For example:

VCL30;VCLDB30;VCLDBX30

Packages listed in the Runtime Packages edit box are automatically linked into your application when you compile. Duplicate package names are ignored, and if the edit box is empty the application is compiled without packages. Runtime packages are selected for the current project only. To make the current choices into automatic defaults for new projects, select the “Default” check box at the bottom of the dialog box.

When you create an application with packages, you still need to include the names of the original Delphi units in the uses clause of your source files. For example, the source file for your main form may begin like this:

unit MainForm;

interface

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

Each of the units referenced in this example is contained in the VCL30 package, but you must keep these references in the uses clause, even if you use VCL30 in your application, or you will get compiler errors.


See Also

About packages

Runtime packages

Deploying packages


Design-time packages

See Also

Design-time packages are used to install components on the IDE’s Component palette and to create special property editors for custom components.

Delphi ships with the following design-time component packages preinstalled in the IDE.

Package	Component palette pages

DCLSTD30.DPL	Standard, Additional, System, Win32, Dialogs

DCLTEE30.DPL	Additional (TChart component)

DCLDB30.DPL	Data Access, Data Controls

DCLMID30.DPL	Data Access (TMIDASConnection component)

DCL31W30.DPL	Win 3.1

DCLNET30.DPL	Internet

DCLISP30.DPL	Internet

DCLSMP30.DPL	Samples

DCLOCX30.DPL	ActiveX

DCLQRT30.DPL	QReport

DCLDSS30.DPL	Decision Cube

IBEVNT30.DPL	Samples (IBEventAlerter component)

These design-time packages work by calling runtime packages, which they reference in their requires clauses. For example, STDLIB references VCL30. STDLIB itself contains additional functionality that makes most of the standard components available on the Component palette.

In addition to preinstalled packages, you can install your own component packages, or component packages from third-party developers, in the IDE. The DCLUSR30 design-time package is provided as a default container for new components.


See Also

About packages

Installing component packages

Creating and editing packages

About package collections


Installing component packages

See Also

In Delphi, all components are installed in the IDE as packages. If you’ve written your own components, you can create and compile a package that contains them. The unit file that contains your component source code must correspond to the model for component source files described in the Delphi Developer’s Guide.

To install or uninstall your own components, or components from a third-party vendor:

1.	If you are installing a new package, copy or move the necessary files to a local directory.

If the package is shipped with separate .DPL, .DCP, and .DCU files, be sure to copy all of them.

If the package is shipped as a .DPC (package collection) file, only the one file need be copied; the .DPC file contains the other files.

The directory where you store the .DCP file—and the .DCU files, if they are included with the distribution—must be in the Delphi Library Path. It is customary to put executable files in the Delphi \BIN directory.

2.	Choose Component|Install Packages from the IDE menu, or choose Project|Options and click the Packages tab.

3.	A list of available packages appears under “Design packages”.

To install a package in the IDE, select the check box next to it.

To uninstall a package, deselect its check box.

To see a list of components included in an installed package, select the package and click Components.

To add a package to the list, click Add and browse in the Open Package dialog box for the directory where the .DPL or .DPC file resides (see step 1). Select the .DPL or .DPC file and click Open. If you select a .DPC file, a new dialog box will appear to handle the extraction of the .DPL and other files from the package collection.

To remove a package from the list, select the package and click Remove.

4.	Click OK.

Unless you change the default settings, new projects are created with all available packages installed. To make the current installation choices into the automatic default for new projects, select the “Defaults” check box at the bottom of the dialog box.

To remove components from the Component palette without uninstalling a package, select Component|Configure Palette, or select Tools|Environment Options and click the Palette tab. The Palette options tab lists each installed component along with the name of the Component palette page where it appears. Selecting any component and clicking Hide removes the component from the palette.


See Also

About packages

Design-time packages

About package collections

Creating and editing packages


Creating and editing packages

See Also

A package source file does not contain type, data, procedure, or function declarations. Instead it contains a name for the package, a list of other “required” packages, and names of the source-code units that should be bound into the package.

To create a package you must specify:

A name for the package.

A list of other packages required by or linked to the new package.

A list of unit files to be contained by, or bound into, the package when it is compiled. The package is essentially a wrapper for these source-code units, which contain the functionality of the compiled DPL.

Package source files, which end with the .DPK extension, are generated by the Package editor.

To create a package:

1.	Load or create a project in the IDE.

2.	Choose File|New, select the Package icon, and click OK.

3.	In the New Package dialog box, enter a file name and (optionally) a description. You can include a directory path with the name; otherwise, the package will reside in the current directory. To open a file/directory selection dialog box, click the Browse button. If you type a file name directly in the New Package dialog box, the .DPK extension will be added automatically.

Package names must be unique within a project. If you name a package “STATS”, the Package editor generates a source file for it called “STATS.DPK”; the compiler generates an executable and a binary image called “STATS.DPL” and “STATS.DCP”, respectively. Use “STATS” to refer to the package in the requires clause of another package, or when using the package in an application.

4.	Click OK to open the Package editor.

5.	Choose the Contains tab, then click the Add speed button.

6.	When the Add dialog box opens, you can add an existing unit file to the package’s contains clause, create a new component that descends from an existing component, or import an ActiveX component.

To add a unit file to the package’s contains clause, choose the Add Unit tab and enter a file in the Unit File Name edit box. You can specify an entire directory path with the file name, or edit the search path in the Search Path edit box; if you edit the search path, you will be changing the global Library Path for Delphi. To add more unit files to the package, close the Add dialog box by clicking OK, then click the Add speed button again in the Package editor.

To create a new component, choose the New Component tab. Enter the new component’s class name, the name of its immediate ancestor, the name of the unit file where the component’s declaration should be written, and the page on the Component palette where you want the new component displayed. You can specify an entire directory path with the file name, or edit the search path in the Search Path edit box; if you edit the search path, you will be changing the global Library Path for Delphi.

To import an ActiveX component, choose the Import ActiveX tab and select a registered component from the list box. To register a new component, click Add. To unregister a component, click Remove.

7.	In the Package editor, choose the Requires tab to see a list of other packages in the requires clause of the package you are editing. VCL30 is listed by default. If you want to add more packages to the requires clause, click the Add speed button and proceed as in step 6 above.

8.	Click the Options speed button and select the Description tab.

Under “Usage options”, check “Design package”, “Runtime package”, or both. Selecting only one of these options turns on the $RUNONLY or $DESIGNONLY compiler directive. (See “Inserting package-specific compiler directives in code”.) To create a design-time package that is not installable in the IDE (but may still be called by other design-time packages), deselect both check boxes.

Under “Build control”, select “Rebuild as needed” or “Explicit rebuild”. “Explicit rebuild” turns on the -Z ($IMPLICITBUILD OFF) compiler switch, which prevents implicit recompilation of the package. (See “Using the command-line compiler”.)

Click OK to close the Package Options dialog box and save your changes.

9.	In the Package editor, click the Build speed button to compile your package.

Clicking the Build speed button compiles your package into a DPL, but does not save the package source (.DPK) file. To save the .DPK file, right-click and select Save Package, or choose File|Save from Delphi’s main menu bar.

Editing an existing package in the Package editor:

There are several ways to open an existing package in the Package editor.

Choose File|Open (or File|Reopen) and select a .DPK file.

Choose Component|Install Packages, select a package from the Design Packages list, and click the Edit button.

Choose Component|Add Unit To Package and click the Add To Existing tab. Then select a package and a unit file to add to the package’s contains clause.

You can also open a package by choosing File|Open and selecting a .DPL file. However, the Package editor always looks for a .DPK file to open, even if you select a .DPL. If it cannot find the .DPK file, the Package editor is sometimes able to open the .DPL on a read-only basis; in this case, you can resave the package as a .DPK for further editing.

The Contains page in the Package editor displays a list of unit files contained in the current package. You can add or remove files by clicking the appropriate speed button. To open a unit file in the Code editor, select the file, right-click, and choose View Unit.

The Requires page displays a list of other packages required by (linked to) the current package. You can add or remove files by clicking the appropriate speed button. To open one of the required packages in a new Package editor, select the package, right-click, and choose View Required Package.

The Package Options speed button lets you edit the package’s description or set compiler and library options.

Editing package source files manually

Package source files, like project files, are generated by Delphi from information you supply. Like project files, they can also be edited manually. A package source file should be saved with the .DPK (Delphi package) extension to avoid confusion with other files containing Object Pascal source code.

To open a package source file in the Code editor:

1.	Open the package in the Package editor.

2.	Right-click in the Package editor and select View Package Source.

For example, the following code declares the VCLDB30 package.

package VCLDB30;

requires VCL30;

contains Db, Dbcgrids, Dbctrls, Dbgrids, Dbinpreq, Dblogdlg, Dbpwdlg, Dbtables;

end.

The package heading specifies the name for the package.

The requires clause lists other, external packages used by the current package. If a package does not reference other packages, then it does not need a requires clause.

The contains clause identifies the unit files to be compiled and bound into the package.


See Also

About packages

Using packages in an application

Compiling packages

Deploying packages

Installing component packages

About package collections


Compiling packages

See Also

You can compile a package from the IDE or from the command line. When you rebuild any project that contains a package, the package is implicitly recompiled if necessary.

To recompile a package by itself from the IDE:

1.	Choose File|Open.

2.	Select “Delphi package source (*.dpk)” from the “Files of type” drop-down list.

3.	Select a .DPK file in the dialog box.

4.	When the Package editor opens, click the Build speed button.

You can insert compiler directives into your package source code. For more information, see “Inserting package-specific compiler directives in code”, below.

If you compile from the command line, new package-specific switches are available. For more information, see Compiling packages.

Inserting package-specific compiler directives in code

The following table lists package-specific compiler directives that you can insert into your source code.

Directive	Purpose

{$IMPLICITBUILD OFF}	Prevents a package from being implicitly recompiled later. Use in .DPK files when compiling packages that provide low-level functionality, that change infrequently between builds, or whose source code will not be distributed.

{$G-} or {IMPORTEDDATA OFF}	Disables creation of imported data references. This directive increases memory-access efficiency, but prevents the unit where it occurs from referencing variables in other packages.

{$WEAKPACKAGEUNIT ON}	Packages unit “weakly”. See “The $WEAKPACKAGEUNIT directive” below.

{$DENYPACKAGEUNIT ON}	Prevents unit from being placed in a package.

{$DESIGNONLY ON}	Compiles the package for installation in the IDE. (Put in .DPK file.)

{$RUNONLY ON}	Compiles the package as runtime only. (Put in .DPK file.)

Note:	Including {$DENYPACKAGEUNIT ON} in your source code prevents the unit file from being packaged. Including {$G-} or {IMPORTEDDATA OFF} may prevent a package from being used in the same application with other packages. Packages compiled with the {$DESIGNONLY ON} directive should not ordinarily be used in applications, because they contain extra code required by the IDE. Other compiler directives may be included, if appropriate, in package source code. See the Object Pascal Language Guide for information on compiler directives not discussed here.

The $WEAKPACKAGEUNIT directive

The $WEAKPACKAGEUNIT directive affects the way a .DCU file is stored in a package’s .DCP and .DPL files. If {$WEAKPACKAGEUNIT ON} appears in a unit file, the compiler omits the unit from DPLs when possible, and creates a non-packaged local copy of the unit when it is required by another application or package. A unit compiled with this directive is said to be “weakly packaged”.

For example, suppose you’ve created a package called PACK that contains only one unit, UNIT1. Suppose UNIT1 does not use any further units, but it makes calls to RARE.DLL. If you put {$WEAKPACKAGEUNIT ON} in UNIT1.PAS when you compile your package, UNIT1 will not be included in PACK.DPL; you will not have to distribute copies of RARE.DLL with PACK. However, UNIT1 will still be included in PACK.DCP. If UNIT1 is referenced by another package or application that uses PACK, it will be copied from PACK.DCP and compiled directly into the project.

Now suppose you add a second unit, UNIT2, to PACK. Suppose that UNIT2 uses UNIT1. This time, even if you compile PACK with {$WEAKPACKAGEUNIT ON} in UNIT1.PAS, the compiler will include UNIT1 in PACK.DPL. But other packages or applications that reference UNIT1 will use the (non-packaged) copy taken from PACK.DCP.

Note:	Unit files containing the {$WEAKPACKAGEUNIT ON} directive must not have global variables, initialization sections, or finalization sections.

The $WEAKPACKAGEUNIT directive is an advanced feature intended for developers who distribute their DPLs to other Delphi programmers. It can help you to avoid distribution of infrequently used DLLs, and to eliminate conflicts among packages that may depend on the same external library.

For example, Delphi’s PenWin unit references PENWIN.DLL. Most projects don’t use PenWin, and most computers don’t have PENWIN.DLL installed on them. For this reason, the PenWin unit is weakly packaged in VCL30. When you compile a project that uses PenWin and the VCL30 package, PenWin is copied from VCL30.DCP and bound directly into your project; the resulting executable is statically linked to PENWIN.DLL.

If PenWin were not weakly packaged, two problems would arise. First, VCL30 itself would be statically linked to PENWIN.DLL, and so you could not load it on any computer which didn’t have PENWIN.DLL installed. Second, if you tried to create a package that contained PenWin, a compiler error would result because the PenWin unit would be contained in both VCL30 and your package. Thus, without weak packaging, PenWin could not be included in standard distributions of VCL30.

Using the command-line compiler

When you compile from the command line, you can use the package-specific switches listed in the following table.

Switch	Purpose

-$G-	Disables creation of imported data references. Using this switch increases memory-access efficiency, but prevents packages compiled with it from referencing variables in other packages.

-LEpath	Specifies the directory where the package DPL file will be placed.

-LNpath	Specifies the directory where the package DCP file will be placed.

-Z	Prevents a package from being implicitly recompiled later. Use when compiling packages that provide low-level functionality, that change infrequently between builds, or whose source code will not be distributed.

Note:	Using the -$G- switch may prevent a package from being used in the same application with other packages. Other command-line options may be used, if appropriate, when compiling packages. See the Object Pascal Language Guide for information on command-line options not discussed here.

Package files created by a successful compilation

To create a package you compile a source file that has a .DPK extension. The base name of the .DPK file becomes the base name of the files generated by the compiler. For example, if you compile a package source file called TRAYPAK.DPK, the compiler creates a package called TRAYPAK.DPL.

The following table lists the files produced by the successful compilation of a package.

File extension	Contents

DCP	A binary image containing a package header and the concatenation of all DCU files in the package. A single DCP file is created for each package. The base name for the DCP is the base name of the DPK source file.

DCU	A binary image for a unit file contained in a package. One DCU is created, when necessary, for each unit file.

DPL	The runtime package. This file is a Windows DLL with special Delphi-specific features. The base name for the DPL is the base name of the DPK source file.


See Also

About packages

Creating and editing packages

Installing component packages

About package collections


packages, deploying;packages;
Deploying packages

See Also

Deploying applications that use packages

When distributing an application that uses runtime packages, make sure that your users have the application’s .EXE file as well as all the library (.DPL or .DLL) files that the application calls. If the library files are in a different directory from the .EXE file, they must be accessible through the user’s Path. You might want to follow the convention of putting library files in the Windows\System directory. If you use InstallShield Express, your installation script can check the user’s system for any packages it requires before blindly reinstalling them.

Distributing packages to other developers

If you distribute runtime or design-time packages to other Delphi developers, be sure to supply both .DCP and .DPL files. You will probably want to include .DCU files as well.


See Also

About packages

Using packages in an application

Runtime packages


package collections; packages, deploying;
About package collections

See Also

Package collections (.DPC files) offer a convenient way to distribute packages to other developers. Each package collection contains one or more packages, including DPLs and any additional files you want distribute with them. When a package collection is selected for IDE installation, its constituent files are automatically extracted from their .DPC container; the Installation dialog box offers a choice of installing all packages in the collection or installing packages selectively.

To create a package collection:

1.	Choose Tools|Package Collection Editor to open the Package Collection editor.

2.	Click the Add Package speed button, then select a DPL in the Select Package dialog box and click Open. To add more DPLs to the collection, click the Add Package speed button again. A tree diagram on the left side of the Package editor will display the DPLs as you add them. To remove a package, select it and click the Remove Package speed button.

3.	Select the Collection node at the top of the tree diagram. On the right side of the Package Collection editor, three fields will appear:

In the File Name edit box, type a name for the package collection file you want to produce. You can include a directory path with the name; otherwise, the package collection file will reside in the current directory. Do not include an extension.

In the Author/Vendor Name edit box, you can enter optional information about your package collection that will appear in the Installation dialog box when users install packages.

Under Directory List, list the default directories where you want the files in your package collection to be installed. Use the Add, Edit, and Delete buttons to edit this list. For example, suppose you want all source code files to be copied to the same directory. In this case, you might enter “Source” as a Directory Name with “C:\MyPackage\Source” as the Suggested Path. The Installation dialog box will display “C:\MyPackage\Source” as the suggested path for the directory.

4.	In addition to DPLs, your package collection can contain .DCP, .DCU, and .PAS (unit) files, documentation, and any other files you want to include with the distribution. Ancillary files are placed in file groups associated with specific packages (DPLs); the files in a group are installed only when their associated DPL is installed. To place ancillary files in your package collection, select a DPL in the tree diagram and click the Add File Group speed button; type a name for the file group. Add more file groups, if desired, in the same way. When you select a file group, new fields will appear on the right in the Package Collection editor:

In the Install Directory list box, select the directory where you want files in this group to be installed. The drop-down list includes the directories you entered under Directory List in step 3, above.

Select the “Optional Group” check box if you want installation of the files in this group to be optional.

Under Include Files, list the files you want to include in this group. Use the Add, Delete, and Auto buttons to edit the list. The Auto button allows you to select all files with specified extensions that are listed in the contains clause of the package; the Package Collection editor uses Delphi’s global Library Path to search for these files.

5.	You can select installation directories for the packages listed in the requires clause of any package in your collection. When you select a DPL in the tree diagram, four new fields appear on the right side of the Package Collection editor:

In the Required Executables list box, select the directory where you want the .DPL files for packages listed in the requires clause to be installed. (The drop-down list includes the directories you entered under Directory List in step 3, above.) The Package Collection Editor searches for these files using Delphi’s global Library Path and lists them under Required Executable Files.

In the Required Libraries list box, select the directory where you want the .DCP files for packages listed in the requires clause to be installed. (The drop-down list includes the directories you entered under Directory List in step 3, above.) The Package Collection Editor searches for these files using Delphi’s global Library Path and lists them under Required Library Files.

6.	To build your package collection, click the Compile speed button.

Clicking the Compile speed button generates a .DPC file with the name you have selected, but does not save the package collection source file. To save the source file for future editing, select File|Save. Package collection source files should be saved with the .PCE extension. To edit or recompile an existing .PCE file, select File|Open in the Package Collection editor.


See Also

About packages


Run menu

The Run menu commands help you debug your program from within Delphi. The following commands form the core functionality of the integrated debugger:

Run	Compiles and executes your application

Parameters	Specifies startup parameters for your application

Register ActiveX	Registers the project in the Windows registry. Available when the current project is an ActiveX project.

Unregister ActiveX Servers	Removes  the project from the Windows registry. Available when the current project is an ActiveX project.

Step Over	Executes a program one line at a time, stepping over procedures while executing them as a single unit

Trace Into	Executes a program one line at a time, tracing into procedures and following the execution of each line

Trace To Next Source Line	Executes the program, stopping at the next executable source line in your code

Run To Cursor	Runs the loaded program up to the location of the cursor in the Code Editor window

Show Execution Point	Positions the cursor at the execution point in an edit window

Program Pause	Temporarily pauses the execution of a running program

Program Reset	Ends the current program run and releases it from memory

Add Watch	Opens the Watch Properties dialog box, where you can create and modify watches

Add Breakpoint	Opens the Edit Breakpoint dialog box, where you can create and modify breakpoints

Evaluate/Modify	opens the Evaluate/Modify dialog box, where you can evaluate or change the value of an existing expression

Note:	The integrated debugger commands become accessible when you generate symbolic debug information for the project you are working on.


Run | Run

See also

Choose Run|Run to compile and execute your application, using any startup parameters you specified in the Parameters dialog box.

If you have modified the source code since the last compilation, the compiler recompiles those changed modules and relinks your application.

If the compiler encounters an error, it displays an Error dialog box. When you choose OK to dismiss the dialog box, the Code Editor places the cursor on the line of code containing the error.

The compiler builds .EXE files according to the following rules:

The project (.DPR) file is always recompiled.

If the source code of a unit has changed since the last time the unit was compiled, the unit is compiled. When a unit is compiled, Delphi creates a file with a .DCU extension for that unit.

If Delphi cannot locate the source code for a unit, that unit is not recompiled.

If the interface section of a unit has changed, all the other units that depend on the changed unit are recompiled.

If a unit links in an .OBJ file (external routines), and the .OBJ file has changed, the unit is recompiled.

If a unit contains an Include file. and the Include file has changed, the unit is recompiled.


See also

The Delphi Debugger

Controlling program execution

Build commands keyboard shortcuts

Compiling, building and running projects


Register or unregister an ActiveX server

See also

Run | Register an ActiveX server

or

Run | Unregister an ActiveX server.

These options are available only for ActiveX projects.

If the project has not been compiled previously, it will be compiled when either of these options is selected.

Register an Active X Server 	Adds the server to the Windows registry file.

Unregister an ActiveX Server 	Removes the server from the Windows registry file.


Run | Parameters

See also

Choose Run|Parameters to open the Parameters dialog box.

Parameters dialog box

Use this dialog box to pass command-line parameters to your application when you run it, just as if you were running the application from the Program Manager File|Run menu.

You can use these parameters with the ParamCountand ParamStr() functions.

Run Parameters	Enter the parameters you want to pass to your application when it starts, or use the drop-down button to choose from a history of previously specified parameters. Parameters take effect only when your application is started. Do not enter the application name in this edit box.


See also

The Delphi Debugger


Run | Step Over

See also

Choose Run|Step Over to execute a program one line at a time, stepping over procedures while executing them as a single unit.

The Step Over command executes the program statement highlighted by the execution point and advances the execution point to the next statement.

If you issue the Step Over command when the execution point is located on a function call, the debugger runs that function at full speed, then positions the execution point on the statement that follows the function call.

If you issue Step Over when the execution point is positioned on the end statement of a routine, the routine returns from its call, and the execution point is placed on the statement following the routine call.

The debugger considers multiple program statements on one line of text as a single line of code; you cannot individually debug multiple statements contained on a single line of text. The debugger also considers a single statement that spans several lines of text as a single line of code.

By default, when you initiate a debugging session with Run|Step Over, Delphi moves the execution point to the first line of code that contains debugging information (this is normally a location that contains user-written code). To step over start-up code that Delphi automatically generates, see Stepping through code.

In addition to stepping over procedures, you can trace into them, following the execution of each line. Use Run|Trace Into to execute each line of a procedure.

An alternative way to perform this command is:
Choose the Step Over speed bar button.


See also

The Delphi Debugger

Controlling program execution

Debug commands keyboard shortcuts


Run | Trace Into

See also

Choose Run|Trace Into to execute a program one line at a time, tracing into procedures and following the execution of each line.

The Trace Into command executes the program statement highlighted by the execution point and advances the execution point to the next statement.

If you issue the Trace Into command when the execution point is located on a function call, the debugger traces into the function, positioning the execution point on the function’s first statement.

If you issue Step Over when the execution point is positioned on the end statement of a routine, the routine returns from its call, and the execution point is placed on the statement following the routine call.

If the execution point is located on a function call that does not have debugging information, such as a library function, the debugger runs that function at full speed, then positions the execution point on the statement following the function call.

By default, when you initiate a debugging session with Run|Trace Into, Delphi moves the execution point to the first line of code that contains debugging information (this is normally a location that contains user-written code). To trace into start-up code that Delphi automatically generates, see Stepping through code.

In addition to tracing into procedures, you can step over them, executing each procedure as a single unit. Use Run|Step Over to execute procedures as a single unit.

An alternative way to perform this command is:
Choose the Trace Into speedbar button.


See also

The Delphi Debugger

Controlling program execution

Debug commands keyboard shortcuts


Trace to Next Source Line
Run | Trace To Next Source Line

See also

Use this command to stop on the next source line in your application, regardless of the control flow. For example, if you select this command when stopped at a Windows API call that takes a callback function, control will return to the next source line, which in this case is the callback function.


See also

The Delphi Debugger

Controlling program execution

Debug commands keyboard shortcuts


Run | Run To Cursor

See also

Choose Run|Run To Cursor to run the loaded program up to the location of the cursor in the Code Editor window.

When you run to the cursor, your program is executed at full speed, then pauses and places the execution point on the line of code containing the cursor.

You can use Run To Cursor to run your program and pause before the location of a suspected problem. You can then use Run|Step Over or Run|Trace Into to control the execution of individual lines of code.

An alternative way to perform this command is:
Choose Run To Cursor from the Code Editor context menu.


See also

The Delphi Debugger

Controlling program execution

Debug commands keyboard shortcuts


Run | Show Execution Point

See also

Choose Run|Show Execution Point to position the cursor at the execution point in an edit window. If you closed the edit window containing the execution point, Delphi opens an edit window displaying the source code at the execution point.


See also

The Delphi Debugger

Controlling program execution

Debug commands keyboard shortcuts


Run | Program Pause

See also

Choose Run|Program Pause to temporarily pause the execution of a running program.

The debugger pauses program execution and positions the execution point on the next line of code to execute. You can examine the state of your program in this location, then continue debugging by running, stepping, or tracing.

If your program assumes control and does not return to the debugger--for example, if it is running in an infinite loop--press Ctrl+Alt+Sys Req to stop your program. You might need to press this key combination several times before your program actually stops, because the command does not work if Windows kernel code is executing.

In addition to temporarily pausing a program running in the debugger, you can also stop a program and release it from memory. Use Run|Program Reset to stop a running program and release it from memory.


See also

The Delphi Debugger

Controlling program execution

Debug commands keyboard shortcuts


Run | Program Reset

See also

Choose Run|Program Reset to end the current program run and release it from memory.

Use Program Reset to restart a program from the beginning, such as when you step past the location of a bug, or if variables or data structures become corrupted with unwanted values.

When you reset a program, Delphi performs the following actions:

Closes all open program files

Releases resources allocated by calls to the VCL

Clears all variable settings

Resetting a program does not delete any breakpoints or watches you have set, which makes it easy to resume a debugging session.

Windows resources

Resetting a program does not necessarily release all Windows resources allocated by your program. In most cases, all resources allocated by VCL routines are released. However, Windows resources allocated by code which you have written might not be properly released.

If your system becomes unstable, through either multiple hardware or language exceptions or through a loss of system resources as a resul of resetting your program, you should exit Delphi before restarting your debugging session.


See also

The Delphi Debugger

Controlling program execution

Debug commands keyboard shortcuts


Run | Add Watch

See also

The Add Watch command opens the Watch Properties dialog box, where you can create and modify watches. After you create a watch, use the Watch List window to display and manage the current list of watches.

Alternate ways to perform this command are:

Choose Add Watch at Cursor from the Code Editor context menu.

Choose Add Watch from the Watch List context menu.

Right-click an existing watch in the Watch List window and choose Edit Watch from the Watch List context menu.


See also

The Delphi Debugger

Watching expressions

Debug commands keyboard shortcuts


Run | Add Breakpoint

See also

The Add Breakpoint command opens the Edit Breakpoint dialog box, where you can create and modify breakpoints.

Alternate ways to perform this command are:

Choose Add Breakpoint from the Breakpoint List context menu.

Right-click an existing breakpoint in the Breakpoint List window and choose Edit Breakpoint from the Breakpoint List context menu.


See also

The Delphi Debugger

Using breakpoints

Controlling program execution

Debug commands keyboard shortcuts


Run | Evaluate/Modify

See also

The Evaluate/Modify command opens the Evaluate/Modify dialog box, where you can evaluate or change the value of an existing expression.

An alternate way to perform this command is:
Choose Evaluate/Modify from the Code Editor context menu.


See also

The Delphi Debugger

Debug commands keyboard shortcuts


Search menu

Search commands keyboard shortcuts

Use the Search menu commands to locate text, errors, objects, units, variables, and symbols in the Code Editor.

Find	Searches for specific text, and highlights first occurrence in the code editor.

Find In File	Searches for specific text, displays each occurrence in a window at the bottom of the code editor.

Replace	Searches for specific text and replace it with new text

Search Again	Repeats search

Incremental Search	Searches for text as you type

Go to Line Number	Moves cursor to specific line number

Find Error	Searches for most recent run-time error

Browse Symbol	Searches for specified symbol


Search | Find

See also	Find In Files Tab

Choose Search|Find to display the Find Text dialog box.

Find Text dialog box

Select the Find tab to specify text you want to locate and to set options that affect the search. Find locates the line of code containing the first occurrence of the string and highlights it.

Text to find

Enter a search string; or, click the down arrow next to the input box to select from a list of previously entered search strings.

Options	Specifies attributes for the search string:

Case sensitive	Differentiates uppercase from lowercase when performing a search.

Whole words only	Searches for words only. (With this option off, the search string might be found within longer words.)

Regular expressions	Recognizes regular expressions in the search string.

Direction	Specifies which direction you want to search, starting from the current cursor position:

Forward	From the current position to the end of the file. Forward is the default Direction setting.

Backward	From the current position, to the beginning of the file.

Scope	Determines how much of the file is searched:

Global	Searches the entire file, in the direction specified by the Direction setting. Global is the default scope.

Selected text	Searches only the selected text, in the direction specified by the Direction setting. You can use the mouse or block commands to select a block of text.

Origin	Specifies where the search should start:

From cursor	The search starts at the cursor's current position, and then proceeds either forward to the end of the scope, or backward to the beginning of the scope depending on the Direction setting. From Cursor is the default Origin setting.

Entire scope	The search covers either the entire block of selected text or the entire file (no matter where the cursor is in the file), depending upon the Scope options.


See also

Block commands keyboard shortcuts (brief)

Block commands keyboard shortcuts (default and classic)

Block commands keyboard shortcuts (Epsilon)

Search commands keyboard shortcuts


Search | Find in file
Search | Find in files

See also 	Find Tab

Choose Search|Find in files to list occurrences of a specified string.

Find in file dialog box

Select the Find in Files tab to specify text you want to locate and to set options that affect the search. Each occurrence of the string is listed in a box at the bottom of the Code Editor. Double-click a list entry to move to that line in the code file.

Text to find

Enter a search string.  To select from a list of previously entered search strings, click the down arrow next to the input box.

Options	Specifies attributes for the search string:

Case sensitive	Differentiates uppercase from lowercase when performing a search.

Whole words only	Searches for words only. (With this option off, the search string might be found within longer words.)

Regular expressions	Recognizes regular expressions in the search string.

Where	Specifies which files you wish to search:

Search all files in project	Searches all files in the open project.

Search all open files	Searches files that are currently open.

Search in directories	When selected, the Search Directories options are available. The search proceeds through all files indicated.

Search directory options	Defines the full path to file(s) to be searched:

File masks	Specify the path of the files to be searched. By default, only .PAS files are searched. To search other files, use a wild-card entry (such as *.* or *.txt) at the end of the path.

Include subdirectories	If selected, subdirectories from the directory path specified are also searched.


See also

Block commands keyboard shortcuts (brief)

Block commands keyboard shortcuts (default and classic)

Block commands keyboard shortcuts (Epsilon)

Search commands keyboard shortcuts


Search | Replace

See also

Choose Search|Replace to display the Replace Text dialog box.

Replace Text

Use this dialog box to specify text you want to search for and then replace with other text (or with nothing).

Most components of the Replace Text dialog box are identical to those in the Find Text dialog box.

Text to find

Enter a search string. To select from a list of previously entered search strings, click the down arrow next to the input box

Replace with

Enter the replacement string. To select from a list of previously entered search strings, click the down arrow next to the input box. To replace the text with nothing, leave this input box blank.

Options	Specifies attributes for the search strings:

Case sensitive	Differentiates uppercase from lowercase when performing a search.

Whole words only	Searches for words only. (With this option off, the search string might be found within longer words.)

Regular expressions	Recognizes specific regular expressions in the search string.

Prompt on replace	Prompts you before replacing each occurrence of the search string. When Prompt on replace is off, the editor automatically replaces the search string.

Direction	Specifies which direction you want to search the file, starting from the current cursor position:

Forward	From the current position, to the end of the file. Forward is the default Direction setting.

Backward	From the current position, to the beginning of the file.

Scope	Determines how much of the file is searched:

Global	The entire file, in the direction specified by the Direction setting. Global is the default scope.

Selected text	Only the selected text, in the direction specified by the Direction setting. To select a block of text, use the mouse or block commands.

Origin	Specifies where the search should start:

From cursor	The search starts at the cursor's current position, and then proceeds either forward to the end of the scope, or backward to the beginning of the scope depending on the Direction setting. From cursor is the default Origin setting.

Entire scope	The search covers either the entire block of selected text or the entire file (no matter where the cursor is in the file), depending upon the Scope options.

Replace All

To replace every occurrence of the search string, click Replace All. If you have checked the Prompt on Replace option, the Confirm dialog box appears on each occurrence of the search string.


Search | Search Again

Choose Search|Search Again to repeat the last Find, or Replace command.

The settings last made in the Find Text or Replace Text dialog box remain in effect when you choose Search Again. For instance, if you have not cleared the Replace Text settings, the Search Again command searches for the string you last specified and replaces it with the text specified in the Replace Text dialog box.


Search | Incremental Search

Choose Search|Incremental Search to bypass the Find Text dialog box by moving the cursor directly to the next occurrence of text that you type.

When you are performing an incremental search, the Code Editor status line reads "Searching For:" and displays each letter you have typed.

For example, if you type "class" the cursor moves to the next occurrence of the word, highlighting each letter as you type it. This behavior continues until a new occurrence of the string is not found or you press Enter or Escape.

Here are some Incremental Search keystroke options:

Option	Effect

Backspace	Remove the last character from the search string and move to the previous match.

F3	Repeat search   (Default keybinding)

Ctrl+L	Repeat search   (Classic keybinding)

Ctrl+S	Repeat search   (Epsilon keybinding)

Shift+F5	Repeat search   (Brief keybinding)


Search | Go to Line Number

Choose Search|Go to Line Number to display the Go To Line Number dialog box.

Go to Line Number dialog box

This dialog box prompts you for the line number you want to find. The current line number and column number are displayed in the Line and Column Indicator on the status bar of the Code Editor.

When this dialog box first appears, the current line number is in the input box.

Enter New Line Number	Specify the line number of the code you want to go to. To select from a list of previously entered line numbers, click the down arrow next to the input box.


Search | Find Error

Choose Search|Find Error to display the Find Error dialog box.

Find Error dialog box

Use this dialog box to specify the address of the most recent run-time error.

Error Address	The address of the most recent run-time error and the error number appears in the runtime error report if it is available.

When you click OK, Delphi recompiles your program and stops at the address location you entered, highlighting the line that caused the run-time error.


Search | Browse Symbol

See also

Choose Search|Browse Symbol to display the Browse Symbol dialog box.

Browse Symbol dialog box

Use this dialog box to open the Object Browser and display the symbol declaration.

Symbol to Find	Enter a symbol name to inspect. To choose from a list of previously entered symbols, click the down arrow. Use the arrow keys to move through the list box.

When you click OK, Delphi opens the ObjectBrowser to display the symbol declaration.

You can go to the source code that defines the symbol or inspect the symbol declaration.


See also

ObjectBrowser window


Regular expressions

Regular expressions are characters that customize a search string. Delphi recognizes these regular expressions:

Character	Description

 ^	A circumflex at the start of the string matches the start of a line.

 $	A dollar sign at the end of the expression matches the end of a line.

 .	A period matches any character.

 *	An asterisk after a string matches any number of occurrences of that string followed by any characters, including zero characters. For example, bo* matches bot, bo and boo but not b.

 +	A plus sign after a string matches any number of occurrences of that string followed by any characters except zero characters. For example, bo+ matches boo, and booo, but not bo or be.

[ ]	Characters in brackets match any one character that appears in the brackets, but no others. For example [bot] matches b, o, or t.

[^]	A circumflex at the start of the string in brackets means NOT. Hence, [^bot] matches any characters except b, o, or t.

[-]	A hyphen within the brackets signifies a range of characters. For example, [b-o] matches any character from b through o.

{ }	Braces group characters or expressions. Groups can be nested, with a maximum number of 10 groups in a single pattern.

  \  	A backslash before a wildcard character tells the Code Editor to treat that character literally, not as a wildcard. For example, \^ matches ^ and does not look for the start of a line.

Note:	Delphi also supports Brief regular expressions if you are using Brief keystroke mappings.


Brief regular expressions

Use these symbols to produce Brief regular expressions:

 <	A less than at the start of the string matches the start of a line.

 %	A percent sign at the start of the string matches the start of a line.

 $	A dollar sign at the end of the expression matches the end of a line.

 >	A greater than at the end of the expression matches the end of a line.

  ?	A question mark matches any single character.

 @	An at sign after a string matches any number of occurrences of that string followed by any characters, including zero characters. For example, bo@ matches bot, boo, and bo.

 +	A plus sign after a string matches any number of occurrences of that string followed by any characters, except zero characters. For example, bo+ matches bot and boo, but not b or bo.

  |	A vertical bar matches either expression on either side of the vertical bar. For example, bar|car will match either bar or car.

  ~	A tilde matches any single character that is not a member of a set.

[ ]	Characters in brackets match any one character that appears in the brackets, but no others. For example [bot] matches b, o, or t.

[^]	A circumflex at the start of the string in brackets means NOT. Hence, [^bot] matches any characters except b, o, or t.

[-]	A hyphen within the brackets signifies a range of characters. For example, [b-o] matches any character from b through o.

{ }	Braces group characters or expressions. Groups can be nested, with the maximum number of 10 groups in a single pattern.

  \  	A backslash before a wildcard character tells the Borland C++ IDE to treat that character literally, not as a wildcard. For example, \^ matches ^ and does not look for the start of a line.


Tools menu

See also

Use the Tools menu to:

View and change environment settings

Modify the list of programs on the Tools menu

Modify templates and experts.

Create and edit package collections.

Create and modify database tables.

Create and edit images.

Tools menu commands:

Environment Options	Specifies Editor, Browser, and configuration preferences, and customizes the appearance of the Component palette.

Repository	Displays the Object Repository dialog box.

Configure Tools	Displays the Tools Options dialog box. Use this dialog box to add programs to, delete programs from, or edit programs on the Tools menu.

Package Collection Editor	Create and edit package collections. Package collections are a convenient way to bundle packages and associated files for distribution to other developers.

Image Editor	Create and edit resource files, icons, bitmaps, and cursor files for use in Delphi applications.

Database Desktop	Create, view, sort, modify, and query tables in Paradox, dBASE, and SQL formats.


See also

Adding programs to the Tools menu

Tools|Tools


Tools|Options
Tools | Environment Options

Choose Tools|Environment Options to display the Environment Options dialog box. Use the pages of this dialog box to specify Editor, Browser, and configuration preferences, and to customize the way components and pages are arranged on the Component palette.

The pages of the Environment Options dialog box are:

Preferences

Library

Editor Options

Display

Colors

Palette

Browser

Code Insight

To change pages in the dialog box:

Click the tab at the bottom of the dialog box that represents the page you want to use.


Tools|Options - Preferences options
Preferences (Tools | Environment Options)

See also	Environment options

Use the Preferences page of the Enviroment Options dialog box to specify your Delphi configuration preferences.

Desktop contents

Select which desktop settings are saved when you exit Delphi.

Option	When selected

Desktop Only	Saves directory information, open files in the editor, and open windows

Desktop And Symbols	Saves desktop information and browser symbol information from the last successful compile

Autosave options

Specify which files and options are saved automatically by the environment or when you run your program. A check mark means it is enabled.

Check box	When checked

Editor Files	Saves all modified files in the Code Editor when you choose Run|Run, Run|Trace Into, Run|Step Over, Run|Run To Cursor, or when you exit Delphi

Desktop	Saves the arrangement of your desktop when you close a project or exit Delphi. When you later open the same project, all files opened when the project was last closed are opened again regardless of whether they are not used by the project.

Form designer

Set grid preferences that make it easier to design forms.

Option	Effect

Display Grid	Makes the dots that show the form grid visible.

Snap To Grid	Automatically aligns components on the form with the nearest gridline. You cannot place a component "in between" gridlines.

Grid Size X	Sets grid spacing in pixels along the x-axis. Specify a higher number (between 2 and 128) to increase grid spacing.

Grid Size Y	Sets grid spacing in pixels along the y-axis. Specify a higher number (between 2 and 128) to increase grid spacing.

Show Component Captions	Select this option to display component captions.

Debugging

Use the Debugging check boxes to select the debugger you want to use and to enable stepping.

Check box	When checked

Integrated Debugging	Uses the Delphi Integrated Debugger

Step Program Block	Causes the debugger to stop at the first unit initialization that contains debug information

Break On Exception	Stops the application when it reaches an exception and displays the following information:

The exception class

The exception message

The location of the execption

When this option is unchecked, exceptions do not stop the running application. If you are stepping your application, you can step through the exception handlers as if going through the code sequentially.

Compiling and Running

Check box	When checked

Show Compiler Progress	Check to see progress reports while your program compiles.

Warn on package rebuild	Display warning when packages are rebuilt during compile.

Minimize On Run	Minimizes Delphi when you run your application by choosing Run|Run. When you close your application Delphi is restored.

Hide Designers On Run	Hides designer windows, such as the Object Inspector and Form window, while the application is running. The windows reappear when the application closes.


See also

The Delphi Debugger

Creating an exception handler

Form

Project options

Saving projects and individual project files


Tools|Options - Library
Library page (Tools | Environment Options), Global page (Package Options dialog box)

See Also

Use this page to specify directories, compiler, and linker options for all packages. Click the down arrow next to the edit boxes to choose from a list of previously entered symbols.

Map file

Select the type of map file produced, if any. The map file is placed in the directory specfied by DCP Directory, and it has a .MAP extension.

Option	Effect

Off	Does not produce map file.

Segments	Linker produces a map file that includes a list of segments, the program start address, and any warning or error messages produced during the link.

Publics	Linker produces a map file that includes a list of segments, the program start address, any warning or error messages produced during the link, and a list of alphabetically sorted public symbols.

Detailed	Linker produces a map file that includes a list of segments, the program start address, any warning or error messages produced during the link, a list of alphabetically sorted public symbols, and an additional detailed segment map. The detailed segment map includes the segment address, length in bytes, segment name, group, and module information.

Messages	Effect

Show Hints 	Causes the compiler to generate hint messages.

Show Warnings 	Causes the compiler to generate warning messages.

Options	Effect

Compile with debug info	Check to compile the package file with debug information. This will make the resulting file larger, but it does not affect memory requirements or performance.

When you compile a package using debug information, you can use the integrated debugger or Turbo Debugger for Windows to debug the library file.

Directories

DPL output directory	Where the compiler should put the compiled package (.dpl) file.

DCP output directory	Specifies a separate directory to contain the .dcp files.

Library path	Specify search paths where compiler can find the source files for the package. The compiler can find only those files listed in Library Path. If you try to build your package with a file not on the library path, you will receive a compiler error.

Aliases

Unit Aliases	Specify alias names for units that may have changed names or were merged into a single unit. The format is <oldunit>=<newunit>. You can separate multiple aliases with semicolons.


See also

Component palette

Using design-time packages


Options|Environment - Palette
Palette (Tools | Environment Options, Component | Configure Palette)

See also	Environment options

Use the Palette page of the Environment Options dialog box to customize the way the Component palette appears. You can rename, add, or remove, or reorder pages and components.

Pages	Lists the pages in the Component palette, in the order in which they currently appear. You can rearrange these pages or view and rearrange their components in the Components list. The last item in the Pages list is.[All]; when you select [All], the Components list shows components from every page as well as hidden components.

Components 	Lists the components on the currently selected Component-palette page in the Pages list. Components may come from installed packages or they may be component templates created with the Component | Create Component Template command. Components appear in their current order on the palette. You can rearrange components, or move them to a different page, br dragging them. When [All] is selected in the Pages list, you can sort by component name, package, or palette page by clicking on the appropriate column heading.

Use the following buttons when an item is selected in the Pages list.

Move Up / Move Down	To change the position of the selected page, click Move Up or Move Down. You can also drag pages to a new position.

Add 	Click Add to display the Add Page dialog box, where you can create new pages on the Component palette. Once you have created a new Component palette page, you can move components from other pages onto it or add new components to it.

Delete 	To remove the selected page from the palette, click Delete. Before you can delete a page, it must be empty of components.

Rename 	Click Rename to display the Rename Page dialog box, where you can rename the selected page.

Default Pages	Click Default Pages to restore pages to their default order and replace all components on their default pages. This button is available when [All] is selected on the Pages list.

Use the following buttons when an item is selected in the Components list.

Hide / Show	To prevent an installed component from appearing on the Component palette, click Hide. To redisplay a hidden component, select [All] on the Pages list, select the hidden component on the Components list, then click Show.

Delete	To delete a component template, click Delete. This button is available only when a component template is selected.

Move Up / Move Down	To change the position of a component on a page, click Move Up or Move Down. You can also drag components to a new position.


See also

Component palette

Using design-time packages

Adding components to Delphi


Rename Page dialog box

See also	Options|Environment - Palette

Use this dialog box to specify a new name for a page on the Component Palette.

(You first need to select the page you want to rename from Pages list box on the Palette page of the Options|Environment dialog box.)

To open this dialog box:

Click the Rename button on the Palette page of the Options|Environment dialog box.

Page Name	Enter the new name for the page in the Page Name edit box.When you click OK, the new name is reflected in the Pages list box, but the new name is not reflected in the Component palette until you click OK in the Environment Options dialog box.

To exit this dialog box without changing the page name, choose Cancel.


See also

Adding components to Delphi


Add Page dialog box

See also	Options|Environment - Palette

Use this dialog box to add a new page to the Component palette.

The new page is added to the end of the Pages list. You can change the position of the page using the Palette page of the Options|Environment Options dialog box.

To open this dialog box:

Click the Add button on the Palette page of the Options|Environment dialog box.

Page Name	Enter the new name for the page in the Page Name edit box. When you click OK, the new name is added in the Pages list box, but the new page is not added to the Component palette until you click OK in the Environment Options dialog box.

To exit this dialog box without changing the page name, choose Cancel.


Tools|Options - Browser
Browser (Tools | Environment Options)

See also	Environment options

Use the Browser page of the Enviroment Options dialog box to specify how the ObjectBrowser functions and what symbol information is displayed.

Symbols Type Filters

Specify what symbol information is displayed in the ObjectBrowser. By default, every check box is selected; therefore all symbol information is displayed.

Choose from these symbols:

Constants	Inherited

Types	Virtual only

Functions and Procedures	Private

Variables	Protected

Properties	Public

Published

Initial View

Specify what you want to see when the ObjectBrowser first opens. There are three choices:

Objects

Units

Globals

At any time during a browser session, you can switch views using the ObjectBrowser context menu.

Display

Use these check boxes to select how much detail you want for a symbol and whether or not you want the display sorted:

Option	What it does

Qualified Symbols 	Displays the qualified identifier for a symbol.  When this option is off, only the symbol name is displayed.

Sort Always 	Displays the symbols in alphabetical order by symbol name. When this option is off, the symbols sort by declaration order.

Note:	If both Qualified Symbols and Sort Always are checked, symbols sort by their symbol name, not their object name.

Object Tree

Collapse Nodes	Specify which branches of the object tree hierarchy you want collapsed or expanded when you start the ObjectBrowser.For this setting to take effect, you must be browsing objects.


See also

ObjectBrowser window


Tools|Options - Editor Options
Editor (Tools | Environment Options)

See also	Environment options

Use the Editor page of the Enviroment Options dialog box to customize the behavior of the Delphi editor.

Editor SpeedSetting

Use the Editor SpeedSettings to configure the editor:

Option	Automatically sets

Default Keymapping	Auto Indent Mode, Insert Mode, Smart Tab, Backspace Unindents, Group Undo, Overwrite Blocks, Use Syntax Highlight

IDE Classic	Auto Indent Mode, Insert Mode, Smart Tab, Backspace Unindents, Cursor Through Tabs, Group Undo, Persistent Blocks, Use Syntax Highlight

BRIEF Emulation	Auto Indent Mode, Insert Mode, Smart Tab, Backspace Unindents, Cursor Through Tabs, Cursor Beyond EOF, Keep Trailing Blanks, BRIEF Regular Expressions, Force Cut And Copy Enabled, Use Syntax Highlight

Epsilon Emulation	Auto Indent Mode, Insert Mode, Smart Tab, Backspace Unindents, Cursor Through Tabs, Group Undo, Overwrite Blocks, Use Syntax Highlight

Editor Options check boxes

Use the following editor options to control text handling in the Code Editor. Check the option to enable it.

Check box	When selected

Auto Indent Mode	Positions the cursor under the first nonblank character of the preceding nonblank line when you press Enter.

Insert Mode	Inserts text at the cursor without overwriting existing text. If Insert Mode is disabled, text at the cursor is overwritten. (Use the Ins key to toggle Insert Mode in the Code Editor without changing this default setting.)

Use Tab Character	Inserts tab character. If disabled, inserts space characters. If Smart Tab is enabled, this option is off.

Smart Tab	Tabs to the first non-whitespace character in the preceding line. If Use Tab Character is enabled, this option is off.

Optimal Fill	Begins every autoindented line with the minimum number of characters possible, using tabs and spaces as necessary.

Backspace Unindents	Aligns the insertion point to the previous indentation level (outdents it) when you press Backspace, if the cursor is on the first nonblank character of a line.

Cursor Through Tabs	Enables the arrow keys to move the cursor to the beginning of each tab.

Group Undo	Undoes your last editing command as well as any subsequent editing commands of the same type, if you press Alt+Backspace or choose Edit|Undo.

Cursor Beyond EOF	Positions the cursor beyond the end-of-file character.

Undo After Save	Allows you to retrieve changes after a save.

Keep Trailing Blanks	Keeps any blanks you might have at the end of a line.

BRIEF Regular Expressions	Uses BRIEF regular expressions.

Persistent Blocks	Keeps marked blocks selected even when the cursor is moved, until a new block is selected.

Overwrite Blocks	Replaces a marked block of text with whatever is typed next. If Persistent Blocks is also selected, text you enter is added to the currently selected block.

Double Click Line	Highlights the line when you double-click any character in the line. If disabled, only the selected word is highlighted.

Find Text At Cursor	Places the text at the cursor into the Text To Find list box in the Find Text dialog box when you choose Search|Find. When this option is disabled you must type in the search text, unless the Text To Find list box is blank, in which case the editor still inserts the text at the cursor.

Force Cut And Copy Enabled	Enables Edit|Cut and Edit|Copy, even when there is no text selected.

Use Syntax Highlighting	Enables syntax highlighting. To set highlighting options, use the Colors page.

Other options	When selected

Block Indent 	Specify the number of spaces to indent a marked block. The default is 1; the upper limit is 16. If you enter a value greater than 16, you will receive an error.

Undo Limit 	Specify the number of keystrokes that can be undone. The default value is 32,767 (32K).

Note:	The undo buffer is cleared each time Delphi generates code.

Tab Stops 	Set the character columns that the cursor will move to each time you press Tab. If each successive tab stop is not larger than its predecessor, you will receive an error. The default tab stops are 9 and 17.

Syntax Extensions 	Specify, by extension, which files will display syntax highlighting information. The default extensions are .PAS, .DPR, .DFM, .INC, and .INT.


See also

Working with the Code editor


Tools|Options - Editor Display
Display (Tools | Environment Options)

See also	Environment options

Use the Display page of the Enviroment Options dialog box to select display and font options for the Code Editor. The sample window displays the selected font.

The new settings take effect when you click OK.

Display and File check boxes

Configure the editor's display and choose how it saves files.

Check box	Effect

BRIEF Cursor Shapes	Uses BRIEF cursor shapes.

Create Backup File	Creates a backup file that replaces the first letter of the extension with a tilde (~) when you choose File|Save.

Preserve Line Ends	Preserves end-of-line position.

Zoom To Full Screen	Maximizes the Code Editor to fill the entire screen. When this option is off, the Code Editor does not cover the Delphi main window when maximized.

Keystroke Mapping

Enables you to quickly switch key bindings.

Mapping	Effect

Default	Uses key bindings that match CUA mappings (default)

Classic	Uses key bindings that match Borland Classic editor keystrokes

Brief	Uses key bindings that emulate most of the standard BRIEF keystrokes

Epsilon	Uses key bindings that emulate a large part of the Epsilon editor

Visible Right Margin 	Check to display a line at the right margin of the Code Editor.

Right Margin 	Set the right margin of the Code Editor. The default is 80 characters. The valid range is 0 to 1024. If you enter a value larger than 1024, an error message appears.

Visible gutter 	Check to display the gutter on the left edge of the Code Editor.

Gutter width 	Set the width of the gutter, default is 32.

Editor Font 	Select a font type from the available screen fonts installed on your system (shown in the list). The Code Editor displays and uses only monospaced screen fonts, such as Courier. Sample text is displayed below the combo box.

Size 	Select a font size from the predefined font sizes associated with the font you selected in the Font list box. Sample text is displayed below the combo box.

Sample text 	Displays a sample of the select editor font and size.


See also

Code Editor

Working with the Code editor


Tools|Options - Editor Colors
Colors (Tools | Environment Options)

See also	Environment options

Use the Colors page of the Enviroment Options dialog box to specify how the different elements of your code appear in the Code Editor.

You can specify foreground and background colors for anything listed in the Element list box. The sample Code Editor shows how your settings will appear in the Delphi Code Editor.

Color SpeedSettings

Enables you to quickly configure the Code Editor display using predefined color combinations. The sample Code Editor shows how your settings will appear in the Delphi Code Editor.

Element	Specifies syntax highlighting for a particular code element. You can choose from the Element list or click the element in the sample Code Editor.

Color 	Sets the foreground (FG) and background (BG) colors for the selected code element.

To select a color using the mouse, choose one of the following methods:

Click a color to select it as the foreground color.

Right-click a color to select it as the background color.

If you choose the same color for the foreground and the background, it is marked as FB. (This is not recommended.)

To select the color using the keyboard:

1.	Use the arrow keys to highlight a color.

2.	Press F to select it as the foreground color, or B to select it as the background color.

Text Attributes checkboxes Specify format attributes for the code element. The attribute options Delphi supports are bold, italic and underline.

Use defaults for 	Display the code element using default Windows system colors (foreground, background, or both). Unchecking either option restores the previously selected color or, if no color has been previously selected, sets the code element to the Windows system color.


See also

Code Editor

Using syntax highlighting


Using syntax highlighting

See also

Syntax highlighting changes the colors and attributes of your text in the code editor, making it easier to quickly identify parts of your code.

To enable syntax highlighting:

On the Editor Options page of the Tools|Environment Options dialog box, check the Use Syntax Highlight option.

To change the syntax highlighting colors for elements of your code:

Use the Editor Colors page of the Tools|Environment Options dialog box.


See also

Code Editor


Tools|Tools
Tools| Configure Tools

Choose Tools|Tools to display the Tools Options dialog box.

Use the Tools Options dialog box to add, delete, or edit programs on the Tools menu.


Tools Options dialog box

Choose Tools| Configure Tools to open the Tools Options dialog box.

Use this dialog box to add programs to, delete programs from, or edit programs on the Tools menu.

Tools Options dialog box

Tools	Lists the programs currently installed on the Tools menu.

Add	Click Add to display the Tool PropertiesToolPropertiesDB dialog box, where you can specify a menu name, a path, and startup parameters for the program.

Delete	Click Delete to remove the currently selected program from the Tools menu.

Edit	Click Edit to display the Tool Properties dialog box, where you can edit the menu name, the path, or the startup parameters for the currently selected program.

Arrow	Use the arrow buttons to rearrange the programs in the list. The programs appear on the Tools menu in the same order they are listed in the Tool Options dialog box.

To add a program to the tools menu:

Choose Add. Delphi displays the Tool Properties dialog box, where you specify information about the application you are adding.

To delete a program from the tools menu:

Select the program to delete, and choose Delete. Delphi prompts you to confirm the deletion.

To change a program on the tools menu:

Select the program to change, and choose Edit. Delphi displays the Tool Properties dialog box with information for the selected program.


See also

Adding programs to the Tools menu


Tool Properties dialog box

Use the Tool Properties dialog box to enter or edit the properties for a program listed on the Tools menu.

To display the Tool Properties dialog box:

Click Add or Edit in the Tool Options dialog box.

Tool Properties	Description

Title 	Enter a name for the program you are adding. This name will appear on the Tools menu. To add an accelerator to the menu command, precede that letter with an ampersand (&). If you specify a duplicate accelerator, Delphi displays a red asterisk (*) next to the program names in the Tool Options dialog box.

Program	Enter the location of the program you are adding. Include the full path to the program.  To search your drives and directories to locate the path and file name for the program, click the Browse button

Working Dir	Specify the working directory for the program. Delphi specifies a default working directory when you select the program name in the Program Edit Box. You can change the directory path if needed.

Parameters	Enter parameters to pass to the program at startup. For example, you might want to pass a file name when the program launches. Type the parameters or use the Macros button to supply startup parameters. You can specify multiple parameters and macros.

Macros	Click Macros to expand the Tool Properties dialog box to display a list of available macros.  You can use these macros to supply startup parameters for your application.

Browse	Click Browse to select the program name for the Program edit box. When you click Browse, the Select Transfer Item dialog box opens.

To add a macro to the list of parameters:

Select a macro from the list and click Insert.


Transfer macros

Use transfer macros to supply startup parameters to a program on the Tools menu.

To display the macros:

Click the Macros button on the Tool Properties dialog box.

Macro	Description

$COL	Expands to the column number of the cursor in the active Code Editor window.

For example, if the cursor is in column 50, at startup Delphi passes "50" to the program.

$ROW	Expands to the row number of the cursor in the active Code Editor window.

For example, if the cursor is in row 8, at startup Delphi passes "8" to the program.

$CURTOKEN	Expands to the word at the cursor in the active Code Editor window.

For example, if the cursor is on the word Token, at startup Delphi passes "Token" to the program.

$PATH	Expands to the directory portion of a parameter you specify. When you insert the $PATH macro, Delphi inserts $PATH() and you specify a parameter within the parentheses.

For example, if you specify $PATH($EDNAME), at startup Delphi passes the path for the file in the active Code Editor window to the program.

$NAME	Expands to the file name portion of a parameter you specify. When you insert the $NAME macro, Delphi inserts $NAME() and you specify a parameter within the parentheses.

For example, if you specify $NAME($EDNAME), at startup Delphi passes the file name for the file in the active Code Editor window to the program.

$EXT	Expands to the file extension portion of a parameter you specify. When you insert the $EXT macro, Delphi inserts $EXT() and you specify a parameter within the parentheses.

For example, if you specify $EXT($EDNAME), at startup Delphi passes the file extension for the file in the active Code Editor window to the program.

$EDNAME	Expands to the full file name of the active Code Editor window.

For example, if you are editing the file C:\PROJ1\UNIT1.PAS, at startup Delphi passes "C:\PROJ1\UNIT1.PAS" to the program.

$EXENAME	Expands to the full file name of the current project executable.

For example, if you are working on the project PROJECT1 in C:\PROJ1, at startup Delphi passes "C:\PROJ1\PROJECT1.EXE" to the program.

$PARAMS	Expands to the command-line parameters specified in the Run Parameters dialog box.

$PROMPT	Prompts you for parameters at startup. When you insert the $PROMPT macro, Delphi inserts $PROMPT() and you specify a default parameter within the parentheses.

$SAVE	Saves the active file in the Code Editor.

$SAVEALL	Saves the current project.

$TDW	Sets up your environment for running Turbo Debugger. For example, this macro saves your project, ensures that your project is compiled with debug info turned on, and recompiles your project if it is not compiled with debug info turned on. Be sure to use this macro if you add Turbo Debugger to the Tools menu.


Select Transfer Item dialog box

Use the Select Transfer Item dialog box to search drives and directories for a program to add to the Tools menu.

To locate a transfer item:

Click the Browse button on the Tool Properties dialog box.

File Name	Enter the name of the file you want to load, or enter wildcards to use as filters in the Files list box.

Files	Displays the files in the current directory that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box.

List Files Of Type	Choose the type of file you want to open. The default file types are .EXE, .COM, and .PIF files. All files in the current directory of the selected type appear in the Files list box.

Directories	Select the directory whose contents you want to view. Files in the current directory that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box appear in the Files list box.


MTools Repository
Tools | Repository

Choose Tools|Repository to display the Object Repository dialog box. Use the Object Repository dialog box to add, delete, and rename pages in the Object Repository. In addition, you can edit and delete Object Repository items.  You can also specify template and expert options for forms and projects

Pages included on the Object Repository correspond to the User Defined pages in the New Items dialog box displayed by File | New… The objects listed are available from the New Items dialog box.

See also

Object Repository dialog box

About the Object Repository


Object Repository dialog box

Choose Tools|Repository to display the Object Repository dialog box.

Object Repository Options

The settings in the Object Repository Options dialog box affect the behavior of Delphi when you begin a new project or create a new form in an open project. When you select an item in the Objects list box, the appropriate options become available at the bottom of the Objects list box. Depending on the item you select, one or more of the options listed below become available.

New form

Main form

You have the option to override these defaults by choosing File|New and selecting from the New Items dialog box.

By default, opening a new project displays a blank form. You can change this default behavior by changing Object Repository options. For more information see Customizing the Object Repository.

Object Repository
Options	Description

Pages	This list box displays the pages in the Object Repository. When you select a page, the items on that page appear in the Objects list box. Select [Object Repository] to view all items in the Object Repository. The pages in the Object Repository correspond to the user defined pages in the New Items dialog box.  Select File | New… to display the New Items dialog box.

Objects	The Objects list box displays the items on the currently selected page of the Object Repository.

Add Page button	To add a new blank page, click the Add Page button. The Add Page  dialog box appears. Type the name of the page you want to add and click OK.

Delete Page button	To remove an empty page from the Object Repository, in the Pages list box, select the name of the page you want to delete and click the Delete Page button

Rename Page button	To rename an item in the Object Repository, in the Pages list box, select the name of the page you want to rename and click the Rename Page button. The Rename PageRenamePageDialogBox dialog box appears. Type the name of the page you want to rename and click OK. The renamed page appears in the Pages list box.

Edit Object	To edit the properties of items in the Object Repository, from the Objects list box, select the item you want to edit and click the Edit Object button. The Edit Object InfoEditObjectInfoDialogBox dialog box appears. Edit the information as desired and click OK.

Delete Object	Use the Delete Object button to remove the selected object from the Object Repository page.

Up/Down arrows	To change the position of the selected page, click the up arrow or the down arrow. You can also move pages by using a drag-and-drop operation.

There are three ways to add an object to a page:

Right click in a form, select Add to repository from the speed menu.

Select Project | Add to repository.

Drag an object listed in the Object column to a page listed in the Page column of the Object Repository.


Add Page dialog box

Use the Add Page dialog box to add a page to the Object Repository. You access the Add Page dialog box from the Object Repository dialog box.

Page name

Type the name of the new page into the Page name text box.


Rename Page dialog box

Use the Rename Page dialog box to rename a page in the Object Repository. You access the Rename Page dialog box from the Object Repository dialog box.

Page name	Type the new name of the page into the Page name text box.


Edit Object Info dialog box

Use the Edit Object Info dialog box to edit information of Object Repository items.

Options	Description

Title 	Displays the title of the selected item.

Description	Displays the description of the selected item.

Page	Displays the current page containing the selected item. To change the page on which the item appears, select a different page from the Page drop-down list.

Author	Displays the name of the Author of the selected item.

Browse button	The icon of the selected item is displayed to the left of the Browse button. Use the Browse button to select a different icon.

To view the item description:

1.	From the File menu select New.

2.	Select an item in the New Items dialog box.

3.	Right-click the mouse.

4.	Select View Details from the context menu.

The item description appears in the Description column.


Adding programs to the Tools menu

See also

The commands on the Tools menu transfer execution to an external application. You can add programs to, delete programs from, or edit programs on the Tools menu.

To add a program to the tools menu:

1.	Choose Tools| Configure Tools.

Delphi displays the Tool Options dialog box, which lists the programs currently on the Tools menu.

2.	Choose Add.

Delphi displays the Tool Properties dialog box.

3.	Specify a title for the program. The title you specify will be listed on the Tools menu.

4.	Specify the program file or choose Browse to select it from a list.

5.	Specify the working directory for the program, if necessary.

6.	Specify startup parameters for the program, if necessary. You can type the parameters or use the Macros button to supply startup parameters. You can specify multiple parameters and macros.

7.	Choose OK.

Delphi closes the Tool Properties dialog box. The new program is on the Tools list in the Tool Options dialog box.

8.	Choose Close.

Delphi closes the Tool Options dialog box. The new program is on the Tools menu.


See also

Tool Properties dialog box

Transfer macros


About Code Insight

Code Insight enhancements to the code editor display context sensitive pop-up windows within the code file.

To enable and configure Code Insight features, use the Environment Options dialog box.  Select Tools|Environment Options… and select the Code Insight page.

Code Insight provides four tools:

Code Completion

When enabled, the Code Completion dialog box is displayed after you enter a class name followed by a period. The list of properties, methods and events appropriate to the class are displayed. Select the item to be entered in your code.

When you enter an assignment statement and press <ctrl><space_bar>, the list of arguments valid for the variable is displayed. Select an argument to be entered in your code. Similarly, you can bring up a list of arguments when typing a procedure, function, or method call and need to add an argument, or when you are typing in an array property (not a genuine array) and you need to type an index expression.

Code Parameters

View the required arguments for a method as you enter it into your code.

When the Code Parameters feature is enabled, a list box is displayed when you enter a method call followed by the opening parenthesis. The syntax for the arguments to the method is displayed.

Code Templates

A set of templates is available to insert common programming statements into your code. Templates can be edited and added.

While working in the code editor, press <ctrl> j to display the code templates defined.

Tooltip Expression Evaluation

To make it easy to see the value of a variable at any point, enable Tooltip Expression Evaluation.  (To enable Tooltip Expression Evaluation, use the Code Insight dialog box.)  When your are compiling using debug mode and the compile process is stopped, you can point to a variable to display the value of the variable at that point.

When optimization is enabled for the compiler, you might sometimes see a blinking bubble that says "Evaluating" rather than the value. Disable optimization when debugging. (Choose Project | Options, select the Compiler page, and uncheck the Optimization box under Code Generation.)


Code insight dialog box

See also

Use the Code Insight dialog box to configure Code Insight options. Code Insight tools are available while you are working in the code editor.

Automatic Feature	When Enabled

Code Completion	Enter a class name followed by a period in a code file. The list of properties, methods and events appropriate to the class will be displayed. You can then select the item to be entered in your code.

Enter an assignment statement and press <ctrl> <spacebar>. A list of arguments that are valid for the variable is displayed. Select an argument to be entered in your code.

Code Parameters	View the syntax of a Delphi method as you enter it into your code.

Tooltip Expression Evaluation	When the compiler is stopped during debug, you can view the value of a variable by pointing to it with your cursor.

Code Completion Delay	Set the duration of the pause before a Code Insight dialog box is displayed.

Code Templates	Available templates are listed by name with a short description. Click a template name to display the code that will be entered in your file when that template is selected. Code displayed in the code window can be edited.

Templates	The Templates box includes a name and short description for each template.

Code	The code box displays the code that will be inserted into a file when the template is selected. The code displayed can be edited.

To edit the name and description:
Select the name you want to edit. Click the Edit button. Edit the name and description fields as needed and click OK.

To edit a template:
When a name is selected, the code to be inserted in the file when the template is selected is displayed in the code window. Use the mouse to move the cursor to the code window and edit the text as needed.

To define the insertion point for a template:
Place a vertical bar in the code statement to define the point to begin insertion when the template is inserted in a code file. The cursor will be placed in the spot defined by the vertical bar.

To add a template:
Click Add.. After entering a Name and Description in the dialog box displayed, click OK. The cursor will move to the code window for you to define the code that will be entered in a file when the template is selected.

To delete a template:
Select the name of the template you want to delete. Click Delete.


See also

About Code Insight


Code completion

The dialog box lists the valid properties, methods, and events for the class entered.

Arguments that are valid for assignment to the variable entered are listed. Select an item from the list followed by three ellipses (…) to open a second list of related arguments compatible with the variable entered in the assignment statement.

Select an item to be entered in your code file in either of two ways:

Scroll through the list if necessary and double-click the item.

Type until the characters entered refer to the entry in the list you wish to include and press return.

Preferences for Code Completion are configured by selecting Tools|Environment Options to open the Environment Options dialog box. Select Code Insight page.


Code template

The templates defined for your installation are listed. Select an item to be entered in your code file in either of two ways:

Use the scroll bar as necessary then double click on the template you want to insert in your code.

Type the name of the template until the characters entered refer to the entry in the list you wish to include. Press return.

Code Templates are defined using the Environment Options dialog box. Select Tools|Environment Options and select the Code Insight page.


View menu

Use the View menu commands to display or hide different elements of the Delphi environment and open windows that belong to the integrated debugger.

Project Manager	Displays the Project Manager

Project Source	Displays the project file in the Code Editor

Object Inspector	Displays the Object Inspector

Alignment Palette	Displays the Alignment Palette

Browser	Displays the Object Browser

Using breakpoints	Displays the Breakpoints List dialog box

Call Stack	Displays the Call Stack dialog box

Watching expressions	Displays the Watch List dialog box

Threads	Displays the threads status box

Modules	Displays the Modules window.

Component List	Displays the Components dialog box

Window List	Displays a list of open windows

Toggle Form/Unit	Toggles between a form and its unit window

Units	Displays the View Unit dialog box

Forms	Dislpays the View Form dialog box

Type Library	Displays the Type Library editor window

New Edit Window	Opens a new Code Editor

SpeedBar	Hides or shows the SpeedBar

Component Palette	Hides or shows the Component Palette


View | Project Manager

Choose View|Project Manager to open the Project Manager. If the Project Manager is already open, it becomes the active window.

Use the Project Manager window to navigate among a project’s files. You can also use the project manager to add, delete, save, or copy a file to the current project. The Project Manager also lists all the units and their associated form used in the current project, including path information.

Note:	Units are listed in the Project Manager even if they are not currently open.

You can position the Project Manager anywhere on your desktop.


View | Project Source

See also

View|Project Source to make the project file for the open project, the active page in the Code Editor. If the project file is not currently open when you choose this command, Delphi will open it for you.


See also

Project file

Project Manager


View | Object Inspector

See also

If you have closed the Object Inspector, choose this command to reopen it. You can also choose View|Object Inspector to toggle between the Object Inspector and the last active form or Code Editor file.

Use the Object Inspector to edit property values and event-handler links.


See also

About the Object Inspector property editors


View | Alignment Palette

See also

Choose View|Alignment Palette to display the Alignment Palette, which you can use to align components to the form, or to each other.

Note

You can also align components by using the Alignment dialog box.


Alignment palette

See also

Use the Alignment palette to align components to the form, or to each other.

The Alignment palette has Tool Help for each button.

Icon	Effect
	Aligns the selected components to the left edge of the component first selected. (Not applicable for single components.)
	Moves the selected components horizontally until their centers are aligned with the component first selected. (Not applicable for single components.)
	Aligns the selected component(s) to the center of the form along a horizontal line.
	Horizontally aligns three or more selected components so that the middle components are equidistantly spaced between the outer components.
	Aligns the selected components to the right edge of the component first selected. (Not applicable for single components.)
	Aligns the selected components to the top edge of the component first selected. (Not applicable for single components.)
	Moves the selected components vertically until their centers are aligned with component first selected. (Not applicable for single components.)
	Aligns the selected component(s) to the center of the form along a vertical line.
	Vertically aligns three or more selected components so that the middle components are equidistantly spaced between the outer components.
	Aligns the selected components to the bottom edge of the component first selected. (Not applicable for single components.)

If you are unsure of how a particular button on the Alignment palette acts, click and hold on the button.  The icon on the button changes to show you how it will align the selected components. To apply the button’s alignment to a selection, relase the button. To prevent alignment after you click and hold the button, drag the mouse off the palette before releasing the mouse button.

Note

You can also use the Alignment dialog box to align components.


See also

Aligning components

Selecting components in the form

Aligning components


View | Watches

Choose View|Watches to open the Watch List window.

The Watch List window displays all the currently set watch expressions.

If you keep this window open during your debugging sessions, you can monitor how your program updates the values of important variables as the program runs.


View | Threads

Choose View|Threads to view the Threads status box.

Use this status box to view the status of all threads currently executing in the application being debugged.


Threads status box

Choose View|Threads to view the Threads status box.

Use this status box to view the status of all threads currently executing in the application being debugged.

When a debug event occurs (breakpoint, exception, paused), the thread status view will indicate the status of each thread as it executes.  Using the local menu, the user can make a different thread current.  When a thread is marked as current, the next step or run operation is relative to that thread.

Threads status box

Thread ID	Displays the OS assigned thread ID.

State	The thread state is either Running or Stopped.

Location	Displays the source position. Displays the address if there is no source location available.

Status

The thread status displays one of the following:

Breakpoint	The thread stopped due to a breakpoint.

Faulted	The thread stopped due to a processor exeception.

Unknown	The thread is not the current thread so its status is unknown.

Stepped	The last step command was successfully completed.


View | Modules

Choose View|Modules to view a list of modules that are loaded into memory when the current project is run.  The Modules window shows each module’s name, its runtime image base address, and a path indicating the location from which the module is loaded..

Here a “module” usually means a DLL or package that is required by the current project at runtime.  Normally, you would open this window after you have compiled a project and are debugging it.  It is also helpful when optimizing to improve load time by specifying preferred image base offsets for each required module.

Note that the runtime image base address is the memory offset, in hexidecimal, where the module actually loads, as distinct from the preferred image base address you may have specified in the Project Options window.


View | Breakpoints

Choose View|Breakpoints to open the Breakpoint List window.

The Breakpoint List window lists all currently set breakpoints.

Each breakpoint listing shows the following:

The file in which the breakpoint is set

The line number of the breakpoint

Any condition associated with the breakpoint

Any pass count associated with the breakpoint


View | Call Stack

Choose View|Call Stack to open the Call Stack window.

The Call Stack window lists the current sequence of routines called by your program. In this listing, the most recently called routine is at the top of the window, with each preceding routine call listed beneath.

Each entry in the Call Stack window displays the procedure name and the values of any parameters passed to it.


View | Browser

See also

Choose View|Browser to open the ObjectBrowser window.

Use the ObjectBrowser to visually explore the objects, units, and hierarchies of classes and methods used in your application.

Note

If the Browser command is disabled, compile your program before opening the ObjectBrowser.


See also

ObjectBrowser window


View | Component List

See also

Choose View|Component List to display the Components window.

Components window

Use this window to add components to your forms using the mouse or keyboard.

Options	Description
Search By Name	Enter the name of the component you want to add. This list box performs an incremental search so that the cursor moves to the first component containing the letters you type.
Component list box	Select the component you want to add. Components are listed in alphabetical order, and their button representation is on the left.
Search edit box	When you select a component, its name appears in the Search edit box.
Add To Form	Click Add To Form to place an instance of the selected component in the center of the form. To select Add To Form from the keyboard, press Enter.
To add the component you selected in the Component list box, do one of the following:

Press Enter.

Double-click the component name.

Click the Add to Form button.

Note

When you add a component to a form by using the keyboad, Delphi uses the default component size and adds the component to the center of the form unless a container component (such as a group box or  panel) is selected.

If a container component is selected, Delphi places the component you are adding in the center of that container. To add a component into a container, you must select the container before selecting Add To Form.


See also

Adding components using the keyboard


View | Toggle Form/Unit

See also

Choose View|Toggle Form/Unit switch between the current form or its unit file.

Alternative ways to perform this command are:

Choose the Toggle Form/Unit button from the Toolbar.

Choose View Unit or View Form from the Project Manager Context menu.


See also

Viewing and manipulating a project's contents

View|Forms

View|Units


View | Units

See also

Choose View|Units to display the View Unit dialog box.

View Unit dialog box

Use this dialog box to view the project file or any unit in the current project. When you choose a unit, it becomes the active page in the Code Editor.

If the unit you want to open is not currently open, Delphi opens it.

Alternative ways to perform this command:

Choose the Select Unit From List button from the SpeedBar.

Choose View Unit from the Project Manager Context menu.


See also

Project file

Unit file

Viewing and manipulating a project's contents


View | Forms

See also

Choose View|Forms to display the View Form dialog box.

View Form dialog box

Use this dialog box to view any form in the current project. When you select a form, it becomes the active form, and its associated unit becomes the active module in the Code Editor.

If the associated unit is not open when you select a form, Delphi opens it.

Alternative ways to perform this command:

Choose the Select Form From List button from the SpeedBar.

Choose View Form from the Project Manager Context menu.


See also

Form

Viewing and manipulating a project's contentsCreating a user interface


View | Window List

Choose View|Window List to display the Window List dialog box.

Window List dialog box

Use this dialog box to make an inactive Delphi window active. If you have a lot of windows open, this is the easiest way to locate a specific window. The Windows List dialog box displays all open Delphi windows.

To select a window, do one of the following:

Double-click the window name.

Select the window name and click OK.


View | New Edit Window

Choose View|New Edit Window to open a new Code Editor that contains a copy of the active page from the original Code Editor.

Any changes you make to either the original or the copy are reflected in both files.

So that you can distinguish between the windows, the caption in the original window is postfixed with a 1, the first copy with a 2, the second copy with a 3, and so on.


View | SpeedBar

Choose View|SpeedBar to show or hide the SpeedBar.

When this command is checked, the SpeedBar is visible.

Alternatively, you can hide the SpeedBar by choosing Hide from the SpeedBar Context menu.


View | Component Palette

Choose View|Component Palette to show or hide the Component palette.

When this command is checked, the Component palette is visible.

Alternatively, you can hide the Component palette by choosing Hide from the Component palette Context menu.


Type Library editor
View | Type Library

See also

The Type Library editor lets you examine and create type information for ActiveX controls, ActiveX servers, and other COM objects.  You can provide type information with an object either stand-alone in a type library (.TLB) file or you can integrate it into the EXE or OCX as a resource.  Including the type library with an application or OCX makes information about the objects it contains available to other applications and programming tools.

When you use the wizards to convert a Delphi component into an ActiveX control, a type library is automatically created for you.  The ActiveX control wizard compiles the type library into the OCX as a resource automatically when you build your control.  Although it is unnecessary to use the Type Library dialog when you use wizards, you may use it to examine or modify the type information created by the wizard.

You would normally need to use the Type Library editor to create a type library file only when you have added custom functionality to a COM object, such as an ActiveX server, and you want to expose this functionality to clients.  The wizard creates type libraries to support only the built-in functionality of Delphi components; you would have to add to the object’s type library file any additional methods, events, and properties you wish exposed to clients.

For an overview of type libraries, with links to topics on using type libraries with the Type Library editor, see About type libraries.

For an in-depth treatment, refer to the online version of the Delphi Developer’s Guide, especially:

Working with type librariesOLE and ActiveX Overview

The main elements of the Type Library editor are:

Toolbar
These buttons add instances of new information types to the current type library.  When you click a button, its icon is added to the object list pane.

Error log pane

Object list pane
Each instance of an information type in the current type library appears in the object list at the right, represented by a unique icon.  Select an icon to see its data pages displayed in the information pane at the right.

Information pane.  For each object selected in the object list pane, this pane may display tabbed pages for Attributes, Uses, or Members:

Type library information

Enumeration type information

Interface and object information

Interface properties or methods information

Component object class information

Note

You can add or modify only interfaces, dispinterfaces, component object classes, enumerations types, properties, and methods.  Although records, variant records, type definitions, and modules are also contained in a type library, they can be viewed but not edited in the Type Library editor.


See also

When to use a type library

Importing a type library

Type Library deployment

Valid types

Creating a new type library

Adding an interface to the type library

Adding properties and methods to the type library

Adding a component object class to the type library

Adding an enumeration to the type library

Saving and registering type library information


Toolbar

The toolbar at the top of the Type Library editor contains buttons that you click to create new instances of various information types in a type library:

Interface	Adds an interface description.

DispInterface	Adds a DispInterface description.

CoClass	Adds a component object class.

Enum	Adds an enumeration.

Method	Adds a method of the interface.

Property	Adds a property of the interface.

Refresh	Regenerates the Pascal type library file in memory.

Register	Registers the current type library in the Windows Registry.

When you click a toolbar button, the icon of that information type appears in the object list pane at the left side of the Type Library editor.  Each click of a button adds an additional instance of that information type to the current type library.  You can then select its icon in the object list pane and view or edit its information in the right pane.  Depending on the type selected, more than one page may appear in the right pane. For more information on the Type Library editor, see “ Working with type libraries”.


Object list pane

The Type Library editor’s object list pane lists all the elements and their type information that are in the library. These elements include interfaces, dispinterfaces, component object classes, interface properties and methods, enumerations types, records, variant records, type definitions and modules. The object list pane is an expandable hierarchical tree where members of a particular interface appear as children to the interface.

The following color-coded symbols appear to the left of the objects in the object list (left) pane:

Icon	Meaning
	A type library. This can be expanded to expose the individual type information, including objects and interfaces.
	An interface description
	A dispinterface description
	A component object class
	A method of the interface
	A property of the interface
	An enumeration
	A record. Read-only.
	A variant record. Read-only.
	A type definition. Read-only.
	A module. Read-only.


Type library information

When a type library is selected in the object list pane, its Attributes and Uses pages appear in the Information pane.  Click the tab of the page you want to see.

Attributes page

The attributes page displays type information about the currently selected library. The following  attributes and flags appear on the Attributes page when a type library is selected in the main object list pane.

Attribute	Meaning
Name	The descriptive name of the type library without spaces or punctuation.
GUID	The globally unique 128-bit identifier of the interface.
Version	A particular version of library in cases where multiple versions of the library exist. The Version is either a pair of decimal integers separated by a period, or a single decimal integer. The first of the two integers represents the major version number, and the second represents the minor version number of an interface. If a single integer is used, it represents the major version number. Both major and minor version numbers are short unsigned integers in the range between 0 and 65535, inclusive.
LCID	The locale identifier that describes the single national language used for all text strings in the type library and elements.
Help File	The name of the help file associated with the type library.
Help String	A short description of the library. It is strongly recommended that you provide a help string.
Help Context	The Help context ID of the library.
The following flags specify how the server is to be used by other applications:

Flag	Meaning
None	No flags.
Restricted	Prevents the library from being used by a macro programmer.
Control	Indicates that the library represents a control from which a container site will derive additional TYPELIBs or component object classes.
Hidden	Indicates that the library exists but should not be displayed in a user-oriented browser.
Uses page

The Uses page lists other type libraries referenced by the currently selected library:

Column	Meaning
Type Library	Reference to another type library.
GUID	The globally unique 128-bit identifier of the referenced type library.


Enumeration type information

When an enumeration type is selected in the object list pane, its Attributes and Members pages appear in the Information pane.

Attributes page

When an enumeration type is selected in the main object pane, the following attributes appear on the Attributes page:

Attribute	Meaning
Name	The name of the enumeration.
GUID	The globally unique 128-bit identifier of the interface.  This is optional.
Help String	A short description of the enumeration. It is strongly recommended that you provide a help string.
Help Context	The Help context ID of the enumeration.
Version	Identifies a particular version of the enumeration in cases where multiple versions of the enumeration exist. The Version is either a pair of decimal integers separated by a period or a single decimal integer. The first of the two integers represents the major version number.  The second integer represents the minor version number of an interface. If a single integer is used, it represents the major version number. Both major and minor version numbers are short unsigned integers in the range between 0 and 65535, inclusive.
Members page

The Members page contains the type definition of the enumeration type.  It may also include a help string and a help context for each enumeration element.

The following is a sample entry of an enumeration type for beveling on a panel and includes a help string for each enumeration element. It is strongly recommended that you provide a help string for your enumerations. Applications that use your COM object may depend on a help string.

bvNone = 0;

helpstring 'bvNone';

bvLowered = 1;

helpstring 'bvLowered';

bvRaised = 2;

helpstring 'bvRaised';


Interface and object information

An interface is a group of semantically-related routines that provide communication between Communication Object Model (COM) components, including ActiveX components and OLE automation.

When an interface or COM object is selected in the object list pane, its Attributes and Members pages appear in the Information pane.

Attributes page

The following attributes and flags appear on the attributes page for an interface or COM object:

Attribute	Meaning
Name	The name of the interface or object.
GUID	The globally unique 128-bit identifier of the interface or object. This attribute distinguishes one interface from other interfaces.
Help String	A brief description of the interface or object.
Help Context	The Help context ID.
Version	Identifies a particular version in cases where multiple versions of the interface or object exist. The Version  is  either a pair of decimal integers separated by a period, or a single decimal integer. The first of the two integers represents the major version number, and the second represents the minor version number of an interface. If a single integer is used, it represents the major version number. Both major and minor version numbers are short unsigned integers in the range between 0 and 65535, inclusive.
Parent Interface	The name of the interface that is the base interface for this interface. Applies only to interfaces.
Flag	Meaning
DispInterface	The interface describes the methods, and properties for an object that must be accessed through IDispatch.Invoke and cannot be accessed through a v-table.
Hidden	Indicates that the interface exists but should not be displayed in a user-oriented browser.
Non-extensible	Indicates that the IDispatch implementation includes only the properties and methods listed in the interface description.
Dual	Identifies an interface that exposes properties and methods through IDispatch and directly through the v-table.
Ole Automation	Indicates that an interface can use only OLE Automation-compatible types. This flag is not allowed on dispinterfaces because they are OLE automation compatible by definition.
Members page for an interface

The Members page defines the property and methods of the interface.  You can also define a DispID for each member. The following sample defines properties contained in an interface:

property Caption: WideString; dispid 1;

property BevelInner: DAXPanelBevel; dispid 2;

property BevelOuter: DAXPanelBevel; dispid 3;

property BevelWidth: Integer; dispid 4;

property BorderWidth: Integer; dispid 5;

property WindowBorder: DAXBorderStyle; dispid 6;

property Font: Font; dispid 7;

The following sample defines methods contained in an interface:

procedure OnClick; dispid 1;

procedure OnEnter; dispid 2;

procedure OnExit; dispid 3;

procedure OnDblClick; dispid 4;

procedure OnKeyPress(var Key: Byte); dispid 5;

In the Interface Definition Language (IDL) used in a type library (.TLB) file, ElemDescs are used to describe information needed for transferring a record element, parameter, or function return value between processes. These include flags such as IDL_FIN, IDL_FOUT, and IDL_FRETVAL.

If the parameter type is preceded by a Pointer type, the Type Library editor will usually translate that type into a variable parameter.  When the type library is saved, the variable parameter's associated ElemDesc's IDL flags are marked (IDL_FIN or IDL_FOUT).

Often, ElemDesc IDL flags are not marked (IDL_FIN or IDL_FOUT) when the type is preceded with a Pointer, or in the case of dispinterfaces, IDL flags are not typically used.  In these cases, you may see a comment next to the variable identifer such as {IDL_None} or {IDL_In}.  These comments are used when saving a type library to correctly mark the the IDL flags.


Component object class information

When an OLE object is selected in the Object List pane, the following columns appear on the Members page, showing the members of the object.

Column	Meaning
Interface	Name of an interface that the COM class will implement.
GUID	Identifies the GUID of the member interface of the object.
Source	Indicates that the member is a source of events.
Default	Indicates that the interface or dispinterface represents the default programmability interface, intended for use by macro languages such as Word Basic, Visual Basic, Delphi, Object PAL, and Excel Basic.
Restricted	Prevents the item from being used by a macro programmer such as Word Basic, Visual Basic, Delphi, Object PAL, and Excel Basic.
To add new interfaces to the COM class:
Select from a context menu that appears when you right-click in this window. The context menu lists the interfaces from which you can choose to add to this class. The list includes interfaces that are defined in the current type library and those defined in any type libraries referenced by the current type library.


Interface members
Interface properties or methods

When a property or method of an interface is selected in the object list pane, its Attributes page appears in the Information pane.

Attributes page

The following attributes and flags appear on the attributes page of a property or method of an interface:

Attribute	Meaning
Declaration	The declaration of the property or method.
ID	Dispatch ID
Help String	Identifies the documentation string of the property or method. This usually contains a descriptive title of item.
Help Context	Identifies the Help context ID of the property or method.
Property Type	Indicates whether the property is Read/Write, ReadOnly, WriteOnly, or By Reference Only
Flag	Meaning
By Reference	Passes the property by reference in addition to by value.
Restricted	Prevents the property or method from being used by a macro programmer.
Source	Indicates that the member returns an object or VARIANT that is a source of events.
Bindable	Indicates that the property supports data binding.
Request Edit	Indicates that the property supports the OnRequestEdit notification.
Display Bindable	Indicates a property that should be displayed to the user as bindable.
Default Bindable	Indicates the single bindable property that best represents the object.
Hidden	Indicates that the property exists but should not be displayed in a user-oriented browser.
Note

OLE properties may have a Write By Reference, which means the property is passed as a pointer rather than by value.  Some applications, such as Visual Basic, use the Write By Reference, if it is present, to optimize performance.  To pass the property only by reference rather than by value, use the property type By Reference Only.  To pass the property by reference as well as by value, click the By Reference flag.


Error log pane

When loading a type library in the Type Library editor, translation errors could occur because the OLE types do not map directly to Pascal types.  When saving or loading a type library, syntax and translation errors are listed in the Error log pane.

For example, Pointer to Pointer to Integer maps to variable Pointer to Integer, but because Pascal requires all parameter to be a named type, this is an illegal type, so this would be translated to variable Pointer.  Because this is not a perfect match, information about the original type is lost when you save the type library.  The Error Log lists these problems, allowing you to correct or note the warnings.


About type libraries

The Communication Object Model uses type libraries to identify what types of objects and interfaces are available on a COM server.

To obtain type information:

Query a running instance of an object for its type information, or

Load and read the type libraries.

Type libraries are files that include information about data types, interfaces, member functions, and object classes exposed by an ActiveX control or server.  Including the type library with an application or OCX makes information about the objects it contains available to other applications and programming tools.

A type library may contain any or all of the following:

Information about data types.

Descriptions of one or more objects, such as an interface or component object class. Each of these descriptions is commonly termed “type info”.

References to type descriptions from other type libraries.

The type library file contains type information for a COM server similar to the way in which a unit, a header file, a DEF file, or an import library contains the necessary information about an application, a DLL, or a Windows EXE. In fact, much of the information previously stored in these files can be stored in a type library.

For more information about type libraries, see the following topics:

When to use a type library

Type Library deployment

Valid types

Creating a new type library

Adding an interface to the type library

Adding properties and methods to the type library

Adding a component object class to the type library

Adding an enumeration to the type library

Saving and registering type library information


When to use a type library

You don’t need to create a type library when you use Delphi wizards to convert a Delphi component into an ActiveX control, because the type library is automatically created for you. You would normally need to use the Type Library editor to create a type library file only when you have added custom functionality to a COM object and you want to expose this functionality to clients.

You could use the wizard to create a Delphi component as an ActiveX control.  Then the automatically generated type library would support only the built-in functionality of Delphi components.  For any additional methods, events, and properties you wish exposed to clients, you would have to add to the object’s type library file.

When defining interfaces for internal use only, within an application, you do not need to create a type library.  You can write the interface definition directly in Object Pascal and add a unit. Chapter 12 of The Object Pascal Language Guide describes the syntax for declaring interfaces in Delphi.

A type library should normally be used for each set of exposed objects. Situations in which a type library is either useful or required are:

Exposed objects that support vtable binding must be described in a type library because vtable references are bound at compile time.

Objects instantiated from classes that support the IProvideClassInfo interface, such as all descendants of the Delphi TTypedComObject class, must have a type library.

ActiveX controls require a type library, which must be included as a resource in the DLL that contains the ActiveX controls.

Type libraries are recommended for applications that use OLE automation for type checking and early binding.

Type libraries are recommended when you need to identify the objects used with Drag-and-Drop, but would not normally otherwise use a type library.


Type library deployment

When you create an ActiveX control by using the ActiveX control wizard, the type library is compiled into the OCX as a resource automatically when you build your control.

Type libraries may be deployed in any of the following forms:

A resource in an .OCX file. This resource should have the type TYPELIB and an integer ID. It must be declared in the resource (.RC) file like this:

1 typelib mylib1.tlb

2 typelib mylib2.tlb

There may be multiple type library resources in a OCX.  Application developers use the resource compiler to add the .TLB file to their own OCX.  An OCX with one or more type library resources typically has the extension .OLB (object library).

A resource in an .EXE file. The file may contain multiple type libraries.

A stand-alone binary file. The .TLB file output by the Type Library editor is a binary file.


Valid types

The following types are valid in a type library. The OLE Automation-compatible column specifies whether the type can be used by an interface that has its OLE Automation or DispInterface flag checked.

Pascal type	OLE variant type	OLE Auto. compat.	Description

Smallint	VT_I2	Yes	2-byte signed integer

Integer	VT_I4	Yes	4-byte signed integer

Single	VT_R4	Yes	4-byte real

Double	VT_R8	Yes	8-byte real

Currency	VT_CY	Yes	currency

TDateTime	VT_DATE	Yes	date

WideString	VT_BSTR	Yes	binary string

IDispatch	VT_DISPATCH	Yes	pointer to IDispatch interface

SCODE	VT_ERROR	Yes	Ole Error Code

WordBool	VT_BOOL	Yes	True = -1, False = 0

OleVariant	VT_VARIANT	Yes	Ole Variant

IUnknown	VT_UNKNOWN	Yes	pointer to IUnknown interface

Shortint	VT_I1	No	1 byte signed integer

Byte	VT_UI1	Yes	1 byte unsigned integer

Word	VT_UI2	No*	2 byte unsigned integer

UINT	VT_UI4	No* 	4 byte unsigned integer

Comp	VT_I8	No	8 byte signed real

Largeuint	VT_UI8	No	8 byte unsigned real

SYSINT	VT_INT	No* 	system dependant integer (Win32=Integer)

SYSUINT	VT_UINT	No* 	system dependant unsigned integer

HResult	VT_HRESULT	No	32 bit error code

Pointer	VT_PTR -> VT_VOID	No	untyped pointer

TSafeArray	VT_SAFEARRAY	No	OLE Safe Array

PChar	VT_LPSTR	No	pointer to Char

PWideChar	VT_LPWSTR	No	pointer to WideChar

Note

Word, UINT, SYSINT, and SYSUINT may be OLE Automation compatible with some applications.

Besides these Pascal types, any interfaces and types defined in the library or defined in referenced libraries can be used in a type library definition.


TSafeArray

In the Type Library editor, a TSafeArray must specify its component type.

For example, in the following code snipit, the TSafeArray specifies a component type of Integer:

procedure HighLightLines(Lines: TSafeArray(Integer));

The component type for a TSafeArray must be an OLE Automation-compatible type. In the Delphi type library translation unit, the component type is not necessary.


Creating a new type library

The standard method for creating type libraries is to write scripts in the Interface Description Language (IDL) or the Object Language (ODL).  However, the Delphi Type Library editor saves you this chore by making it easy to create type libraries for ActiveX controls, ActiveX servers, and other COM objects.

To create a new type library:

1.	Choose File|New to open the New Items dialog box.

2.	Choose the ActiveX page, which opens the New page.

3.	Select the Type Library icon.

4.	Click OK.

The Type Library editor opens with a prompt to enter a name for the type library.

5.	Enter a name for the type library and press Enter.

You can now add interfaces, component object classes, enums, properties, and methods to the type library.


Adding an interface to the type library

To add an interface:

1.	On the toolbar, click the interface icon.

An interface is added to the object list pane, prompting you to add a name.

2.	Type a name for the interface in the interface and press Enter.

The new interface contains defaults and attributes that can be modified as needed. The members page is empty and you can now add properties and methods to suit the purpose of the interface.


Adding properties and methods to the type library

To add interface members to an interface:

1.	Select the interface member to add.

2.	On the toolbar, click either the property or method icon.

An interface member is added to the object list pane, prompting you to add a name.

3.	Type a name for the member and press Enter.

The new member contains default attributes in its attributes page.  These can be modified to suit the member.

You can also add properties and methods by typing directly into the members page. For example, you could type the following property declarations into the members page of an interface:

property AutoSelect: WordBool; dispid 1;

property AutoSize: WordBool; dispid 2;

property BorderStyle: BorderStyle; dispid 3;

After you have added the members to an interface member page, the members appear as separate items in the object list pane, each with its own attributes page where you can modify each new member's attributes.


Adding a component object class to the type library

To add a component object class to a type library:

1.	On the toolbar, click the component object class icon.

A component object class is added to the object list pane, prompting you to add a name.

2.	Type a name for the class and press Enter.

The new class contains default attributes in its attributes page.  These can be modified to suit the class. The members page is blank.

To add members:

1.	Right-click in the members page for the class.

A context menu displays a list of interfaces from which you can choose. The list includes interfaces defined in the current type library and in any type libraries referenced by the current type library.

2.	Double-click the interface you want the class to implement.

The interface is added to the page with along with its GUID and other attributes.


Adding an enumeration to the type library

To add enumerations to a type library:

1.	On the toolbar, click the enum icon.

An enum type is added to the object list pane, prompting you to add a name.

2.	Type a name for the member and press Enter.

The new enum is empty and contains default attributes in its attributes page.  These can be modified to suit the enum.  An empty members page for the enum is blank.

Add values to the enum by typing directly into the members page. For example, you could type the following declarations into the members page of an enum used by a button control:

mbLeft = 0;

helpstring 'mbLeft';

mbRight = 1;

helpstring 'mbRight';

mbMiddle = 2;

helpstring 'mbMiddle';


Saving and registering type library information

Type library information is stored in a .TLB file which is an OLE compound document file.

Delphi also stores type library information in a Pascal file. The .PAS file is a unit that contains the interface, type, and class information of the type library. You should not edit this file because any changes you make will be overridden the next time you save the type library.

When you use Delphi wizards to create an ActiveX control, automation object or ActiveForm, a type library is automatically created along with an implementation unit. The implementation unit is automatically updated when the type library is saved or refreshed.

You can store type library information from the Type Library editor in several ways:

Save

Refresh

Register


Saving a type library

To save the type library, choose File|Save.

Saving a type library does the following:

Performs a syntax check.

Saves information to a .TLB file.

Saves information to a PAS file.

Notifies the module manager to update the implementation if the type library is associated with an ActiveForm, ActiveX control, or Automation object.


Refreshing the type library

To refresh the type library, choose the Refresh icon on the toolbar.

Refreshing the type library does the following:

Performs a syntax check.

Regenerates the Pascal type library file in memory only. Does not save to disk.

Notifies the module manager to update the implementation if the type library is associated with an ActiveForm, ActiveX control, or Automation object.

Note:	If you have renamed or deleted items from the type library, refreshing the implementation might create duplicate entries. In this case, you would need to move your code to the correct entry and delete any duplicates.


Registering the type library

To register the type library, click the Register button on the toolbar.

Registering the type library does the following:

A syntax check is performed.

An entry is added to the Windows Registry for the type library.


Using DDE

See also

Dynamic Data Exchange (DDE) sends data to and receives data from other applications. With Delphi, you can use this data to exchange text with other applications. You can also send commands and macros to other applications, so your application can control other applications.

Here is a typical way to use DDE: a link between two applications is established, either by your application or the other application. Once this link (called a conversation) is established, the two applications can continuously and automatically send text data back and forth. When the text changes in one application, DDE automatically updates the text in the other.

To understand DDE applications, you need to become familiar with the concept of DDE conversations.

When to use DDE

You want to use DDE when exchanging distinct text strings. If all you want to know is the bottom line of a profits spreadsheet, it makes sense to link the cell that contains the bottom line to a Delphi DDE client application.

You could then output the data in an edit box or label. DDE protects the data in the spreadsheet by not allowing the user to activate and edit the spreadsheet from your client application.

Note:	Not all applications support DDE. To determine whether an application supports DDE, refer to its documentation.


See also

Creating DDE client applications

Creating DDE server applications


DDE conversations

See also

DDE conversations consist of a DDE client application and a DDE server application. With Delphi, you can create both DDE clients and DDE servers. In fact, a single Delphi application can be both a DDE client and a DDE server at the same time.

A DDE conversation is defined by the following three characteristics:

DDE services

DDE topics

DDE items

Note:	See the documentation for the DDE server for specific information about specifying the services, topics, or items of a conversation.

DDE services

The service of a conversation is usually the name of the DDE server application's main executable file without the .EXE extension.

Sometimes the service name can differ from the main executable file name.

When the server is a Delphi application, the service is the project name without the .DPR or .EXE extension.

Note:	Sometimes DDE services are called application names. The terminology is interchangeable.

DDE topics

The topic of a DDE conversation is a unit of data, identifiable to the server, containing the linked text. Typically, the topic is a file.

When the server is a Delphi application, the topic is either the Caption of the form containing the data you want to link (if a TDDEServerConv component has not been used) or the Name of the DDE server conversation component (if a TDDEServerConv component has been used).

DDE items

The item of a DDE conversation identifies the actual piece of data to link, for example, spreadsheet cells or database fields.

The syntax used for specifying the DDE item depends on the DDE server application.

When the server is a Delphi application, the item is the Name of the linked TDDEServerItem component.


See also

Creating DDE client applications

Creating DDE server applications

Using DDE


Creating DDE client applications

See also	Example

You can create a DDE client by adding a DDE client conversation (TDDEClientConv) component and a DDE client item (TDDEClientItem) component to a form.

Client applications can poke data (send data to the server) with the PokeData or PokeDataLines method.

Clients can control the server by running it or sending macros with the ExecuteMacro or ExecuteMacroLines method.

To create a DDE client,

1.	Add a DDE client conversation component (TDDEClientConv) and a DDE client item component (TDDEClientItem) to a form.

2.	Assign the name of the conversation component to the DDEConv property of the client item component.

To establish a link at design time, choose this value from a list of possible conversations for DDEConv in the Object Inspector.

To establish a link at run time, your application must execute code that assigns the value to the DDEConv property.


Example

The following example links an item component named DDEClientItem1 to a conversation component named DDEClientConv1:

DDEClientItem1.DDEConv := 'DDEClientConv1';


See also

Controlling other applications with DDE

Creating DDE server applications

Establishing a link with a DDE server

Poking data

Using DDE


Establishing a link with a DDE server

If you have access to the DDE server application and data, you can establish a DDE link by pasting it from the Clipboard at design time.

To establish a DDE link at design time,

1.	Activate the server application and select the data to link to your client application.

2.	Copy the data and DDE link information to the Clipboard from the server application by choosing Copy from the Edit menu of the server.

3.	Activate Delphi and select the DDE client conversation component.

4.	Click the ellipsis button for either the DDEService or the DDETopic property in the Object Inspector.

The DDE Info dialog box appears.

5.	Choose Paste Link.

The service and topic fill in with the correct values automatically. If the Paste Link button is disabled, then the application you intended to be the server does not support DDE, or the DDE information was not successfully copied to the Clipboard.

6.	Choose OK.

The DDEService and DDETopic properties now contain the appropriate values to establish a DDE link.

7.	Select the DDE client item component and choose the name of the linked DDE client conversation component for the DDEConv property from the list in the Object Inspector.

8.	If the Clipboard still contains the DDE link information, choose the appropriate value for the DDEItem property from the list in Object Inspector. Otherwise, type the correct value for the DDEItem property.

To establish a DDE link at run time,

1.	Specify the DDE service and topic with the SetLink method of the DDE client conversation component.

The following example establishes a link to a Borland Paradox 5.0 table named GADGETS.DB in the working directory:

DDEClientConv1.SetLink('PDOXWIN', ':WORK:GADGETS.DB');

2.	Assign the item to the DDEItem property of the DDE client item component.

The following example establishes a link to the Price field of the Paradox table:

DDEClientItem1.DDEItem := 'PRICE';


Processing DDE linked data

Example

Before you can process data from a DDE server, you first need to establish a DDE link.

After establishing a DDE link, the linked data appears in the Text property of the DDE client item component. (If the data is too long to be stored in a string, it is stored in the Lines property. The data is continuously updated by the DDE server, and an OnChange event of the client item component occurs whenever the data changes.

To process linked text data,

1.	Add an edit box (Tedit component) to your form.

2.	Write a statement that assigns the value of the Text property to the Text property of the edit box.

Attach the assignment statement to the OnChange event handler of the DDE client item.


Example

The following event handler assigns the text of the DDE client item to an edit box.

procedure TForm1.DDEClientItem1.Change(sender: TObject);

begin

  Edit1.Text := DDEClientItem1.Text;

end;


Poking data

Example

Poking data means sending data from your DDE client application to the DDE server application, which is opposite the usual data flow direction for DDE.

To poke data, call the PokeData method of a DDE client conversation component. To poke text data that is too long to be contained in a string, use PokeDataLines.

PokeData has two parameters:

The first parameter specifies the item of the DDE conversation (specified in the DDEItem property of the associated DDE client item component).

The second parameter is a string containing the text to send.


Example

The following example sends the text 'Hello' from a DDE client conversation component named DDEClientConv1 to a linked DDE server. The string is inserted into the DDE item specified in the DDEItem property of the DDE client item component named DDEClientItem1:

DDEClientConv1.PokeData(DDEClientItem1.DDEItem, 'Hello');


Controlling other applications using DDE

Example

All DDE client applications can control DDE server applications. When your DDE client tries to establish a link with a DDE server that is not running, the client activates the server and loads the conversation topic (specified in the DDETopic property).

The ConnectMode property of a DDE client conversation component has two possible values:

Value	When active

ddeAutomatic	Your client will run the server upon run-time creation of the form containing the DDE client conversation component.

ddeManual	Your application must execute the OpenLink method of the DDE client conversation component.

Using macros

Another way to control other applications is to execute macro commands. Use the ExecuteMacro method of the DDE client conversation component to send a string containing one or more macro commands to the server. The server then processes the macro. To send a list of macro strings to the DDE server, use ExecuteMacroLines

Note:	Not all DDE servers can process macros. See the documentation for the server application to determine whether it supports macros and for its macro syntax.


Example

The following example uses macros to tell Microsoft Excel 4.0 to close its active worksheet by executing the following code in your client application, assuming your DDE client conversation component is named DDEClientConv1:

DDEClientConv1.ExecuteMacro('[FILE.CLOSE()]', False);


Creating DDE server applications

Example

DDE server applications respond to DDE client. Typically, they contain data that the client application needs to access. Servers simply update clients.

If you want to handle macros sent by the DDE client, use both a TDDEServerItem and a TDDEServerConv to create the DDE server. Then, you can use the OnExecuteMacro event of the DDE server conversation component to process the macro. Also, use both components if you want the Name of the DDE server conversation component to be the topic of the DDE conversation. With only a DDE server item component, the topic of the conversation is the Caption of the form containing the DDE server item.

To create a DDE server using only a DDE server item component,

Add a DDE server item (TDDEServerItem) component to a form.

To create a DDE server using a DDE server conversation component,

1.	Add a DDE server conversation (TDDEServerConv) component and a DDE server item component to a form.

2.	Assign the name of the conversation component to the ServerConv property of the item component.

To establish a link at design time, choose this value from a list of possible conversations for ServerConv in the Object Inspector.

To establish a link at run time, your application must execute code that assigns the value to the ServerConv property.


Example

The following example links an item component named DDEServerItem1 to a conversation component named DDEServerConv1:

DDEServerItem1.ServerConv := 'DDEServerConv1';


Establishing a link with a DDE client

Example

Linking a DDE server to a DDE client enables your client application to share data with the client application.

To establish a DDE link,

1.	Use the CopyToClipboard method of the DDE server item component to copy the value of the Text property (or Lines property), along with DDE link information, to the Clipboard.

2.	Insert the linked data into the DDE client application. Typically, do this by choosing the appropriate command (such as Edit|Paste Special or Edit|Paste Link) of the client application.

Note:	The method for establishing a DDE link depends on the DDE client application. See the documentation for the client for specific information about establishing DDE links. If the DDE client is another Delphi application, see Establishing a link with a DDE server.


Example

The following example creates a link from a DDE server item component named DDEServerItem1 to a WordPerfect 6.0 document. If you do not have WordPerfect, this example is worth examining because the steps required are probably similar for any other DDE client application that can paste links.

1.	At run time, your DDE server application should execute the following code:

DDEServerItem1.CopyToClipboard;

2.	Activate WordPerfect and choose Edit | Paste Special.

The WordPerfect Paste Special dialog box appears.

3.	Choose Paste Link.

The Paste Special dialog box closes, and the linked text from the Value property of DDEServerItem1 will appear at the insertion point in the WordPerfect document. When the Value property changes, the text in the WordPerfect document will be updated automatically.


Using the Printer object

See also	Example	TPrinter reference

The Printer object provides several methods and properties that enable you to control the printing of documents from your application. These methods and properties interact with the Print and Printer Setup common dialog boxes.

Canvas

The canvas represents the surface of the currently printing document. You assign the contents of your text file to the Canvas property of the printer object by using AssignPrn. The printer object then directs the contents of the Canvas property (your text file) to the printer.

Fonts

Represents the list of fonts supported by the current printer. These fonts appear in the Font list of the Font dialog box.

Any font selected from this dialog box is reflected back into the Font property for the memo component that contains the text you want to print. However, the printer object has no such relationship to the Font dialog box or to the Font property for the memo. Unless your program specifies otherwise, the printer uses the default (System) font that is returned by the Windows device driver to print your text file.

To change the printer's font,

Assign the Font property for the memo component (or other component whose text you want to print) to the Font property for the printer object's Canvas. This downloads the selected font to the printer.


See also

Printing the contents of a memo


Printing the contents of a memo

See also	Example

To print the contents of a memo component,

1.	Assign a text-file variable to the printer by calling AssignPrn.

2.	Create and open the output file by calling Rewrite.

Any Write or Writeln statements sent to the file variable are then written on the Canvas of the printer object.

The AssignPrn procedure is declared in the Delphi Printers unit, so you must add Printers to the uses clause of the unit that calls AssignPrn.

When the printer is ready for input, you can write the contents (Lines property) of the memo to the printer.


See also

Using the printer object


Example

The following example prints the contents of a Memo field when the user chooses File|Print.

procedure TForm1.Print1Click(Sender: TObject);

var

  Line: Integer;

  PrintText: TextFile;   {declares a file variable}

begin

  if PrintDialog1.Execute then

  begin

    AssignPrn(PrintText);   {assigns PrintText to the printer}

    Rewrite(PrintText);     {creates and opens the output file}

    Printer.Canvas.Font := Memo1.Font;  {assigns Font settings to the canvas}

    for Line := 0 to Memo1.Lines.Count - 1 do

      Writeln(PrintText, Memo1.Lines[Line]);	{writes the contents of the Memo1 to the printer object}

    CloseFile(PrintText); {Closes the printer variable}

  end;

end;


Accessing and editing menus at run time

See also

While you use the Delphi Menu Designer to visually design your application menus, the underlying code is what makes the menus ultimately useful. Each menu command needs to be able to respond to an OnClick event, and there are many times when you want to change menus dynamically in response to program conditions.

You can design your own application menus, or use the predesigned menu templates included with Delphi. For information about how to design menu bars and pop-up (local) menus, see Designing Menus.

The following topics describe how to associate code with menu events at design time, and how to access and modify menus in your running application.

Associating menu events with code

Manipulating menu items at runtime

Merging menus

Disabling menu items

Opening a dialog box with a menu command


See also

TPopupMenu component

Providing a list of open documents in a menu


Opening a dialog box with a menu command

See also

To open a dialog box with a menu command, you can call the Execute method of the dialog box in response to the menu item's OnClick event. For example, the following event-handler code calls the Open File common dialog box when the user selects the application's File|Open command, (assuming the command's Name property has been set to FileOpen).

procedure TForm1.FileOpenClick(Sender: TObject);

begin

  OpenFileDialog1.Execute;

end;

Of course, you still need to specify how you want your application to interact with the dialog box once it is open.


See also

Accessing and editing menus at run time

Associating menu events with code

Manipulating menu items at runtime

Manipulating open child windows


String List editor

See also

Use the String List editor at design time to add, edit, load, and save strings into any property that has been declared as TStrings.

A string-list property appears in the Object Inspector with (TStrings) in the value column. When you double-click {TStrings}, the String List editor appears.

The String List Speed Menu contains the following commands:

Load

Click Load to display the Load String List dialog box, where you can select an existing file to read into the String List editor.

Save

Click Save to write the current string list to a file. Delphi opens the Save String List dialog box, where you can specify a directory and file name.


See also

Working with string lists


Load String List dialog box
Load … (String List Speed Menu)

Use the Load string list dialog box to specify a predefined string list that has been saved as a text file to load into a property.

Dialog box options

File Name

Enter the name of the file you want to load or wildcards to use as filters in the Files list box.

Files

Displays the files in the current directory that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box.

List Files Of Type

Choose a filter to display the different types of files. By default the text files (*.TXT) for the current directory are displayed in the Files list box.

Directories

Select the directory whose contents you want to view. In the current directory, files that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box appear in the Files list box.

Drives

Select the current drive. The directory structure for the current drive appears in the Directories list box.


Save String List dialog box
Save … (String List Speed Menu)

Use the Save string list dialog box to store the string list from the String List editor into a text file.

Dialog box options

File Name

Enter the name of the file you want to save or wildcards to use as filters in the Files list box.

Files

Displays the files in the current directory that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box.

List Files Of Type

Choose a filter to display the different types of files. By default the text files (*.TXT) for the current directory are displayed in the Files list box.

Directories

Select the directory whose contents you want to view. In the current directory, files that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box appear in the Files list box.

Drives

Select the current drive. The directory structure for the current drive appears in the Directories list box.


Picture editor

Use the Picture editor to select an image to add to any of the graphic-compatible components and to specify an icon for your form.

The Glyph property appears in the Object Inspector when you select a graphic compatible component. Click the ellipse (…) in the value box to display the Picture editor.

Note: To open the Picture editor from an Image component, you can also double-click the component in the form.

The Picture Editor provides the following commands.

Load

Display the Load Picture dialog box, where you can select an existing file to read into the Picture editor. For more information about loading images into the Picture editor, see Loading an image at design time.

Save

Display the Save Picture As dialog box, where you can specify a directory and file name in which to store the image.

Clear

Remove the association between the current image and the selected component.


Load Picture dialog box

See also

Use the Load Picture dialog box to select an image to add to any of the graphic-compatible components and to specify an icon for your form.

To open the Load Picture dialog box,

In the Picture editor, click Load.

Dialog box options

File Name

Enter the name of the file you want to load or wildcards to use as filters in the Files list box.

Files

Displays the files in the current directory that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box.

List Files Of Type

Choose a filter to display the different types of image files. By default the icon files (*.ICO) for the current directory are displayed in the Files list box.

Directories

Select the directory whose contents you want to view. In the current directory, files that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box appear in the Files list box.

Drives

Select the current drive. The directory structure for the current drive appears in the Directories list box.


See also

Loading an image at design time


Save Picture As dialog box

Use the Save Picture As dialog box to store the image loaded in the Picture editor into a new file or directory.

To open the Save Picture As dialog box,

In the Picture editor, click Save As.

Dialog box options

File Name

Enter the name of the file you want to load or wildcards to use as filters in the Files list box.

Files

Displays the files in the current directory that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box.

List Files Of Type

Choose filter to display the different types of image files. By default the icon files (*.ICO) for the current directory are displayed in the Files list box.

Directories

Select the directory whose contents you want to view. In the current directory, files that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box appear in the Files list box.

Drives

Select the current drive. The directory structure for the current drive appears in the Directories list box.


Notebook editor

Use the Notebook editor to add, edit, remove, or rearrange pages in either a TabbedNotebook component or Notebook component. You can also use the Notebook editor to add or edit Help context numbers for each notebook page.

The Notebook editor displays the current pages of the notebook in their current order, and it also displays the Help context associated with that page.

To open the Notebook editor,

1.	Place a Notebook component or TabbedNotebook component on the form.

2.	With that component selected, do one of the following:

Click the ellipsis button in the Value column for the Pages property.

Double-click the Value column for the Pages property.

Edit

Click Edit to open the Edit Page dialog box, where you can modify the page name and Help context number for the selected notebook page.

Add

Click Add to open the Add Page dialog box, where you can create a new notebook page.

Delete

Click Delete to remove the selected page from the notebook.

Move Up/Move Down

Click Move Up or Move Down to rearrange the order of the selected page or pages.


Edit Page dialog box

See also

Use the Edit Page dialog box to edit existing notebook pages from either the Notebook component or the TabbedNotebook component.

To open this dialog box,

In the Notebook editor, click Edit.

Dialog box options

Page Name

Enter the name of the notebook page. There is a 255-character limit on page names.

Help Context

Enter the context ID number for the notebook page. This number is significant if you want to have context-sensitive help for the individual pages of the notebook. The Help context is optional.


See also

Page controls

Notebook editor

Windows 3.1 components


Add Page dialog box

See also

Use the Add Page dialog box to add notebook pages to either the Notebook component or the TabbedNotebook component.

To open this dialog box,

In the Notebook editor, click Add.

Dialog box options

Page Name

Enter the name of the notebook page. There is a 255-character limit on page names.

Help Context

Enter the context ID number for the notebook page. This number is significant if you want to have context-sensitive Help for the individual pages of the notebook. The Help context is optional.


See also

Page controls

Notebook editor

Windows 3.1 components


Input Mask editor

See also

Use the Input Mask editor to define an edit box that limits the user to a specific format and accepts only valid characters. For example, in a data entry field for telephone numbers you might define an edit box that accepts only numeric input. If a user then tries to enter a letter in this edit box, your application will not accept it.

Use the Input Mask editor to edit the EditMask property of the MaskEdit component.

To open the Input Mask editor,

1.	Place a MaskEdit component on the form.

2.	With that component selected, do one of the following:

Click the ellipsis button in the Value column for the EditMask property.

Double-click the Value column for the EditMask property.

Input Mask

Define your own masks for the edit box. You can use special character to specify the mask; for a listing of those characters, see the EditMask property.

The mask consists of three fields separated by semicolons. The three fields are

The mask itself; you can use predefined masks or create your own.

The character that determines whether or not the literal characters of the mask are saved as part of the data.

The character used to represent a blank in the mask.

Save Literal Characters

Check to store the literal characters from the edit mask as part of the data. This option affects only the Text property of the MaskEdit component. If you save data using the EditText property, literal characters are always saved.

This check box toggles the second field in your edit mask.

Character For Blanks

Specify a character to use as a blank in the mask. Blanks in a mask are areas that require user input.

This edit box changes the third field of your edit mask.

Test Input

Use Test Input to verify your mask. This edit box displays the edit mask as it will appear on the form.

Sample Masks

Select a predefined mask to use in the MaskEdit component. When you select a mask from this list, Delphi places the predefined mask in the Input Mask edit box and displays a sample in the Test Input edit box. To display masks appropriate to your country, choose the Masks button.

Masks

Choose Masks to display the Open Mask File dialog box, where you choose a file containing the sample masks shown in the Sample Masks list box.


See also

TMaskEdit component


Masked Text editor

See also

Use the Mask Test editor to enter Values into the edit mask.

Use the Masked Text editor to edit the Text property of the MaskEdit component.

To open the Masked Text editor,

1.	Place an MaskEdit component on the form.

2.	With that component selected, do one of the following:

Click the ellipsis button in the Value column for the Text property.

Double-click the Value column for the Text property.

Input Text edit box

Enter initial values for the MaskEdit component. You can overwrite these values at run time.

Edit Mask label

Displays the mask definition for the current component.


See also

EditText property

TMaskEdit component

About Input Mask editor


Font editor

See also

Use the Font editor to specify, at design time, a font and other font attributes for the selected component or form. Changes you make using the Font editor are reflected in the Font property for a component.

To open the Font editor,

1.	Select any component or the form.

2.	Do one of the following:

Click the ellipsis button in the Value column for the Font property or one of the other properties listed below that use the Font editor.

Double-click the Value column for the Font property or one of the other properties listed below that use the Font editor.

Font

Select a font from the list of all the available fonts you can use in your application.

Font Style

Select a style for the font. This combo box displays only those styles that are available for the selected font. For most of the available fonts there are four possible styles:

Regular

Italic

Bold

Bold Italic

Size

Select a size for the font. This combo box displays only those font sizes that are available for the selected font.

Effects

Check these options to make the text strikethrough or underlined.

Color

Select a color for the font. This combo box lists all the available colors for the selected font.

Sample area

Displays a sample of the selected font before you apply it to the components. The font within this area is updated with every change you make to the font settings.

Script

Lists the available language scripts for the selected font.


See also

TFontDialog component


Color editor

Use the Color editor to specify or define a color for the selected component. Changes you make using the Color editor are reflected in the Color property for a component.

To open the Color editor,

1.	Select any component or the form.

2.	Double-click the Value column for the Color property or one of the other properties listed below that use the Color editor.

Basic Colors grid

Displays selection of standard colors. Click a color to apply it to the selected component.

Custom Colors grid

Displays the colors that you have created. You can create custom colors by clicking Define Custom Colors.

Define Custom Colors

Click Define Custom Colors to expand the Color editor to show options that enable you to create your own colors.

Color field

Displays the spectrum of available colors. The crosshairs indicate the current color. For example, the crosshairs look like this when the color is a shade of yellow:

Click anywhere or drag in the color field to select a color. When you select a color here and then click Add To Custom Color, the selected color is added to one of the Custom Color boxes so you can use it again.

Color|Solid

Displays the currently selected color and its closest solid color. Double-click the solid color to make it the current color.

Hue

Enter a value for the hue. Hue is the "actual" color, for example, red, yellow-green, or purple. Hue refers to the color without regard to saturation or brightness (luminosity).

Sat(uration)

Enter a value for the saturation. Saturation refers to how much gray is in the color. The Sat(uration) field shows the saturation from 0 (medium gray) to 240 (pure color).

Note:	Saturation affects how clear the color is. Luminosity affects how bright the color is.

Lum(inosity) and the Luminosity Slider Control

Enter a value for the luminosity, or drag the pointer on the slide to set the luminosity. Luminosity is the brightness of a color. The Lum(inosity) field shows the luminosity from 0 (black) to 240 (white). The column to the right of the color field shows the range of luminosity for the current color. Slide the arrow to the right of the column up or down to adjust the luminosity. When you change the luminosity, the Red/Green/Blue color values also change.

Red/Green/Blue

Enter values for the proportion of red, green, and blue you want in your color. The values in these fields show the balance of red, green, and blue in the current color. This is sometimes called the RGB color. The range of available values for an RGB color is 0 to 255.

Add To Custom Colors

Click to add the color you have defined to the Custom Color grid on the Color editor.


Following section followed the first para in the Insert Object dialog box topic, but have jumps to ObjClass and ObjDoc property which do not appear to be in the VCL.

To open the Insert Object dialog box,

1.	Place an OLEContainer component on the form.

2.	With that component selected, right click to display the speed menu and select Insert Object.


Insert Object dialog box

Use the Insert Object dialog box at design time to insert an OLE object into an OLEContainer component. The OLEContainer component enables you to create applications that can share data with an OLE server application. After you insert an OLE server object in your application, you can double-click the OLEContainer component to start the server application.

Select whether or not you want to create a new file using the associated OLE server or use an existing file. If you use an existing file, it must be associated with an application that can act as an OLE server.

Create New

Choose Create New to specify that you wish to launch a server application to create a new OLE object. After choosing Create New, the ObjectType list box is displayed.

Object Type

Select an application that you want to use as the OLE server. This list box displays all available applications that can be used as an OLE server. After you select an application, Delphi launches that application.

Create From File

Choose Create From File to specify that the OLE object has already been saved as a file.  After choosing Create From File, the  File, Browse and Link controls are displayed.

File

Enter the fully qualified path for the file you want to insert into your application. The file you choose must be associated with an application that can be used as an OLE server.

Browse

Click Browse to display the Browse dialog box, where you can select a file to use as the OLE server.

Link

Check Link to link the object on the form to a file. When an object is linked, it is automatically updated whenever the source file is modified. When Link is unchecked, you are embedding the object, and changes made to the original are not reflected in your container.

Display As Icon

Check to display the inserted object as an icon on the form. When this option is checked, the Change Icon button is displayed.

Change Icon

Click Change Icon to open the Change Icon dialog box, where you can specify an icon and label for the object you inserted onto the form.


Browse dialog box

The Browse dialog box has multiple uses depending on where you open it. You can use the Browse dialog box for either of the following:

To load an existing file into the OLE container. The file you select must be associated with an application that can be used as an OLE server.

To select an icon to represent an OLE object on the form.

To open the Browse dialog box, do one of the following:

Click Browse in the Insert Object dialog box when you have Create From File selected.

Click Browse in the Change Icon dialog box.

Dialog box options

Source

Enter the name of the file you want to load or wildcards to use as filters in the Files list box.

Files

Displays the files in the current directory that match the wildcards in the Source edit box or the file type in the List Files Of Type combo box.

List Files Of Type

Choose the type of file you want to use as the OLE server. By default all files in the current directory are displayed in the Files list box.

Directories

Select the directory whose contents you want to view. In the current directory, files that match the wildcards in the Source edit box or the file type in the List Files Of Type combo box appear in the Files list box.

Drives

Select the current drive. The directory structure for the current drive appears in the Directories list box.


Change Icon dialog box

Use the Change Icon dialog box to specify an icon and a label for the object you are placing on the form.

To open the Change Icon dialog box,

1.	On the Insert Object dialog box, check Display As Icon.

2.	Click Change Icon.

Icon Radio

Select which icon you want to use. There are three options:

Option	When selected

Current	Uses the current icon.

Default	Uses the default icon.

From File	Enables you to specify an icon using a fully qualified path name. If you do not know the icon name or the path, click Browse to open the Browse dialog box. The display box below the edit box shows all the available icons in the specified file. To choose an icon, select it.

Label

Enter a label that will appear below the icon on the form.

Browse

Click Browse to open the Browse dialog box, where you can select an icon to represent the inserted object on the form.

Sample Icon display

Displays how the icon and label will appear on the form.


Paste Special dialog box

Use the Paste Special dialog box to insert an object from the Windows Clipboard into your OLE container.

Source label

Displays the path of the file you are going to paste.

Paste/Paste Link Radio

Select Paste to embed the object on the form. When you embed an object on a form, your container application stores all the information for the object. It is not necessary to have an external file.

Select Paste Link to link the object to the form. When you link an object to a form, the main source is stored in a file so that when you update the object, the source file is also updated.

As

Lists the type of application object you are pasting. The application listed is the source application from which you received the object that you are pasting.


DDE Info dialog box

See also

Use the DDE Info dialog box to specify, at design time, a DDE server application and a topic for a DDE conversation.

To open the DDE Info dialog box,

1.	Place a DDEClientConv component on the form.

2.	With the component selected, do one of the following:

Click the ellipsis button in the Value column for the DdeService property or DdeTopic property.

Double-click the Value column for the DdeService property or DdeTopic property.

Dialog box options

Dde Service

Specify the server application for the DDE conversation. The application you specify is entered into the Value column for the DdeService property.

You do not need to specify an extension for the server application.

If the directory containing the application is not on your path, you need to specify a fully qualified path.

Dde Topic

Enter the topic for a DDE conversation. The topic is a unit of data, identifiable to the server, containing the linked text. For example, the topic could be the file name of a Quattro Pro spreadsheet.

When the server is a Delphi application, the topic is the name of the form containing the data you want to link.

If the directory containing the topic is not on your path, you need to specify a fully qualified path.

Paste Link

Click Paste Link to paste the application name and file name from the contents of the Clipboard into the App and File edit boxes.

This button is active only when the Clipboard contains data from an application that can be a DDE server.


See also

Creating DDE client applications

DDE conversations

Establishing a link with a DDE server


Filter editor

See also

Use the Filter editor to define filters for the OpenDialog component and the SaveDialog component. These common dialog boxes use the value of Filters in the List Files Of Type combo box to display certain files in the Files list box.

Use the Filter editor to edit the Filter property.

To open the Filter editor,

1.	Place an OpenDialog component or SaveDialog component on the form.

2.	With that component selected, do one of the following:

Click the ellipsis button in the Value column for the Filters property.

Double-click the Value column for the Filters property.

Filter Name column

Enter the name of the filter you want to appear in the Files Of Type combo box.

Filter column

Enter wildcards and extensions that will define your filter. For example, *.TXT would display only files with the .TXT extension.

To apply multiple file extensions to your filter, separate them using a semicolon (;).


See also

TOpenDialog component

TSaveDialog component


Select Directory dialog box
Select Directory dialog box (ReportSmith)

See alsoThe Report component is included for compatibility purposes only.  The Quick Report components contained on the QReport tab of the component palette extend the capabilities of the Report component.

Use the Select Directory dialog box to choose a directory from which you can load or store a ReportSmith report.

Use the Select Directory dialog box to edit the ReportDir property for the Report component.

To open the Select Directory dialog box,

1.	Place a Report component on the form.

2.	With that component selected, do one of the following:

Click the ellipsis button in the Value column for the ReportDir property.

Double-click the Value column for the ReportDir property.

Dialog box options

Directory Name label

Displays the fully qualified path name of the current directory.

Directories

Select a directory where you want to store or load a report. In the current directory, all the files are displayed in the Files list box.

Files

Displays all the files in the current directory selected in the Directories list box. These files are dimmed.

Drives

Select an active drive. The directory structure for the selected drive appears in the Directories list box.

Network

If you are running under Windows for Workgroups or Windows NT, you can click the Network button to open the Connect Network Drive dialog box.


See also

TReport component


Open dialog box

See also

Use the Open dialog box at design time to load a report into the Report component or a multimedia file into the MediaPlayer component.

To open the Open dialog box,

1.	Place a Report component or a MediaPlayer component on the form.

2.	With that component selected, do one of the following:

Click the ellipsis button in the Value column for any of the properties listed below.

Double-click the Value column for either of the properties listed below.

Dialog box options

File Name

Enter the name of the file you want to load or wildcards to use as filters in the Files list box.

Files

Displays the files in the current directory that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box.

List Files Of Type

Choose the type of file you want to load. When you are loading a report, the default file extenstion is .RPT. When you are loading a multimedia file, all files in the current directory are displayed. However, you can limit the display to wave files, midi files, or Windows video files.

Directories

Select the directory whose contents you want to view. In the current directory, files that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box appear in the Files list box.

Drives

Select the current drive. The directory structure for the current drive appears in the Directories list box.


See also

TReport component

TMediaPlayer component


Loading an image at design time

Use the Delphi Picture editor to load images onto any of several graphic-compatible components and to specify an icon to represent a form when it is minimized at run time.

Each graphic-compatible component has a Glyph property that uses the Delphi Picture editor.

To load an image at design time,

1.	Add a graphic-compatible component to your form.

2.	To automatically resize the component so that it fits the graphic, set the component's AutoSize property to True before you load the graphic.

3.	In the Object Inspector, select the Glyph property.

4.	Either double-click in the Value column, or choose the ellipsis button to open the Picture editor.

(To open the Picture editor from an Image component, you can also double-click the component in the form.)

5.	Choose the Load button to open the Load Picture dialog box.

6.	Use the Load Picture dialog box to select the image you want to display, then choose OK.

The image you choose is displayed in the Picture editor.

7.	Choose OK to accept the image you have selected and exit the Picture Editor dialog box.

The image appears in the component on the form.

Note:	When loading an graphic into an Image component, you can automatically resize the graphic so that it fits the component by setting the Image component's Stretch property to True. (Stretch has no effect on the size of icon (.ICO) files.)


Viewing nested properties

Properties can have properties of their own, called nested properties. For example, the Font property of the Label component has nested properties, one of which is Style; the Style property in turn has its own nested properties.

Properties with nested properties show a plus (+) sign on their left side in the Object Inspector. You need to view these nested properties to set them.

To view nested properties,

Choose one of the following methods:

Double-click any property with a plus sign next to it.

The plus sign next to the top-level property changes to a minus (-) sign, and the nested properties are displayed.

To hide nested properties,

Double-click a property with a minus sign next to it.


Collection Editor dialog box

See Also

The Collection Editor dialog box is used to edit the items maintained by a collection object.  A collection object is a descendant of TCollection. The Collection Editor displays information about the items in the collection, and allows you to add, remove, or rearrange the individual items.  For some types of collection, additional buttons are provided to allow other manipulations of the list.

The items displayed in the list window of the Collection Editor can be selected using the mouse.  Once an item is selected, its properties and events can be set using the object inspector.  The following table indicates what properties of the items are displayed in the list window for each type of collection item, and how the collection is used.

Collection	Item type	Properties displayed	Use
TCheckConstraints	TCheckConstraint	ImportedConstraint, or, if no ImportedConstraint is blank, CustomConstraint	Represents record-level constraints for the data in a BDE dataset.  (Constraints property)
TCoolBands	TCoolBand	Text	Represents a set of bands in a CoolBar component.  (Bands property)
TDBGridColumns	TColumn	FieldName	Represents the field binding and display properties of a column in a data-aware grid.  (Columns property)
THeaderSections	THeaderSection	Text	Represents the display properties of the sections in a HeaderControl object.  (Sections property)
TListColumns	TListColumn	Caption	Represents the columns of a report-style List View component.  (Columns property)
TStatusPanels	TStatusPanel	Text	Represents the individual panels of a StatusBar component.  (Panels property)
TWebActionItems	TWebActionItem	Name, PathInfo, Enabled, and Default	Represents the action items that create the responses to HTTP request messages for a Web dispatcher or Web module. (Actions property)

To display the Collection Editor dialog box, first place the component that uses the collection on a form. Select the property that is implemented using the collection (listed in parentheses in the preceding table), and click on the ellipses.  For some components, the Collection Editor may also be displayed by right-clicking the component, and selecting the appropriate editor from the context menu.

Dialog box options

Item list

The Item list displays the properties listed in the third column of the preceding table for each item in the collection. The properties for a selected item are displayed in the Object Inspector and are edited there.

Add button

Adds a new item to the collection. You can select the item and edit its parameters in the Object Inspector.

Delete button

Removes the selected item from the collection.

Move Up/Down buttons

Change the order of the items. For most collections, the order determines the order in which items are displayed or used by the object that maintains the collection.

Add All Fields button (TDBGridColumns only)

Add a column for every field in the dataset to which the data-aware grid is bound.  This button is only enabled if the data-aware grid is bound to an active dataset.

Restore Defaults button (TDBGridColumns only)

Restore the default properties (obtained from the field component) of the currently selected column.  This button is enabled if the currently selected column is bound to a field (the FieldName property is set).

Read From Dictionary button (TCheckConstraints only)

Add a CheckConstraint object for every record-level constraint in the data dictionary.  Each CheckConstraint object will have its ImportedConstraint property set to the constraint from the dictionary.


Working with text

See also

Almost all applications manipulate text in some manner, ranging from providing word-processing capabilities to the user to simply displaying text in a label or menu item that the user can't modify.

The Delphi Memo and Edit components enable the user to read and write text at run time.

Choose a topic for more information.

Displaying and editing text in a memo control

Setting text alignment and word wrap

Using the Clipboard with text


See also

Displaying and editing text in a Memo control

Displaying and editing fields in an edit box

Setting component properties.

TDBMemo component

TDBEdit component


Setting text alignment and word wrap

See also

Alignment and WordWrap are properties of the Memo component. As with all properties, you can set their values during run time with a simple assignment statement.

The following topics discuss ways to set text alignment and word wrap at run time.

Setting text alignment

Setting word wrap


See also

Displaying and editing text in a memo control


Setting text alignment

See also

You set the initial text alignment at design time by setting the component's Alignment property. You can also let your users specify the type of text alignment they prefer at run time.

The following code refers to a menu item called Text that contains commands for Left, Right, and Center alignment. The code specifies that when the Left menu item receives the Click event, the text in the memo field gets aligned to the left, and the Left command gets checked in the menu.

procedure TForm1.AlignLeft;

begin

   MemoLeft.Checked := True;

   MemoRight.Checked := False;

   EditCenter.Checked := False;

   Memo1.Alignment := taLeft;

end;

Only one menu command should be checked at any time, so the previous code ensures that the other commands are unchecked.


See also

Setting word wrap

Displaying and editing text in a memo control


Setting word wrap

See also	Example

The WordWrap property is True by default for the Memo component. The Memo can contain both vertical and horizontal scroll bars, which is the setting you might choose if word wrap were False. Word Wrap is often set as a toggle at run time.

You set the initial value of the ScrollBars property for a Memo component at design time. You might change the ScrollBars property at run time depending on the Memo.WordWrap setting. The example code illustrates one way to accomplish this.


See also

Setting text alignment

Displaying and editing text in a memo control


Example

The following example uses a Character menu with a Word Wrap item that the user can change dynamically to turn word wrap on and off. A check mark next to the menu item indicates that word wrap is on.

The OnClick event handler sets the value of the Memo component's WordWrap property as a toggle. Whenever the user selects the Word Wrap command, the value of the WordWrap property changes to its inverse. If WordWrap was True, it becomes False; if False, it becomes True.

The event handler adds either vertical scroll bars or both vertical and horizontal scroll bars to the Memo component based on the value of the WordWrap property.

Finally, it sets the Checked property to the value of the WordWrap property: if WordWrap is True, then the Checked property is also set to True. Checked is a Boolean property for menu items: If True, a check mark appears next to that menu item.

procedure TEditForm.SetWordWrap(Sender: TObject);

begin

  with Memo1 do

  begin

    WordWrap := not WordWrap;

    if WordWrap then

      ScrollBars := ssVertical else

      ScrollBars := ssBoth;

    WordWrap1.Checked := WordWrap;

  end;

end;


Using the Clipboard with text

See also

Most text-handling applications provide users with a way to move selected text between documents, including documents in different applications. The Clipboard object in Delphi encapsulates the Windows Clipboard and includes methods that provide the basis for operations such as cutting, copying, and pasting text (and other formats).

The Clipboard object is declared in the Delphi Clipbrd unit. Before you can access methods declared in the Clipboard object, you need to add Clipbrd to the uses clause of any units that will use those methods.

Choose from the following topics for more information:

Selecting text

Cutting, copying, and pasting text

Deleting text


See also

Using the Clipboard with graphics

Displaying and editing text in a memo control


Selecting text

See also	Example

Before you can send any text to the Clipboard, the text must be selected. The function of reading and displaying selected text is native to the Memo and Edit components. In other words, you don’t need to write code so that the Memo component can display selected text; it comes with this behavior.

The Stdctrls unit in Delphi provides several methods to work with selected text. (Recall that Delphi automatically adds the Stdctrls unit to the uses clause of any unit whose form contains a component declared within Stdctrls.) SelText, a run-time only property, contains a string based on any text selected in the component. The SelectAll method selects all the text in the memo or other component. The SelLength and SelStart properties return values for a selected string's length and starting position, respectively.


See also

Cutting, copying, and pasting text

Deleting text

Using the Clipboard with text

Displaying and editing text in a memo control


Example

The following code selects all text in a memo component. This could be an event handler, for example, for a Select All menu item.

procedure TEditForm.SelectAll(Sender: TObject);

begin

  Memo1.SelectAll;

end;


Cutting, copying, and pasting text

See also	Example

The following methods cut, copy, and paste text:

CutToClipboard cuts selected text from a memo or edit field and also places it on the Clipboard.

CopyToClipboard copies all selected text in a memo or edit field to the Clipboard.

PasteFromClipboard copies all text currently on the Clipboard back to the location of the insertion point.


See also

Selecting text

Deleting text

Using the Clipboard with text

Displaying and editing text in a memo control


Example

The following OnClick event handlers cut, copy, and paste selected text from a memo component to the Clipboard. These event handlers could be used on an Edit menu for the Cut, Copy, and Paste commands.

procedure TEditForm.CutToClipboard(Sender: TObject);

begin

  Memo1.CutToClipboard;

end;

procedure TEditForm.CopyToClipboard(Sender: TObject);

begin

  Memo1.CopyToClipboard;

end;

procedure TEditForm.PasteFromClipboard(Sender: TObject);

begin

  Memo1.PasteFromClipboard;

end;


Deleting text

See also	Example

The ClearSelection method provides you with a way to remove selected text from a memo component without copying the selected text to the Clipboard.

Contrast this with the CutToClipboard method, which deletes selected text and also copies it to the Clipboard.


See also

Selecting text

Cutting, copying, and pasting text

Using the Clipboard with text

Displaying and editing text in a memo control


Example

The following event handler deletes selected text from a memo component without copying the text selection onto the Clipboard.

procedure TEditForm.Delete(Sender: TObject);

begin

  Memo1.ClearSelection;

end;


SQL Monitor

The SQL Monitor enables you to see the actual statement calls made through SQL Links to a remote server or through the ODBC socket to an ODBC data source.

To open the SQL Monitor,

Choose Database|SQL Monitor.

You can elect to monitor different types of activity. Choose Options|Trace Options to bring up the Trace Options dialog box. This dialog allows you to select from different categories of activities to monitor. You can monitor any number of the following categories:

Category	Displays

Prepared Query Statements	Prepared statements to be sent to the server.

Executed Query Statements	Statements to be executed by the server. Note that a single statement may be prepared once and executed several times with different parameter bindings.

Input Parameters	Parameter data sent to servers when doing INSERTs or UPDATEs.

Fetched Data	Data retrieved from servers.

Statement Operations	Each operation performed such as ALLOCATE, PREPARE, EXECUTE, and FETCH

Connect / Disconnect	Operations associated with connecting and disconnecting to databases, including allocation of connection handles, freeing connection handles, if required by server.

Transactions	Transaction operations such as BEGIN, COMMIT, and ROLLBACK (ABORT).

Blob I/O	Operations on Blob datatypes, including GET BLOB HANDLE, STORE BLOB, and so on.

Miscellaneous	Operations not covered by other categories.

Vendor Errors	Error messages returned by the server.  The error message may include an error code, depending on the server.

Vendor Calls	Actual API function calls to the server.  For example, ORLON for Oracle, ISC_ATTACH for InterBase.

The Buffer page of the Trace Options dialog allows you to specify how much trace information can be maintained in memory by the SQL monitor.  You can choose to have all trace information maintained in a circular memory buffer, so that once the limit is reached, additional traces replace the first traces.  Alternately, you may choose to write traces information to a disk file when the memory buffer becomes full.


Working with string lists

See also

There are numerous occasions when a Delphi application needs to deal with lists of character strings. Among these lists are the items in a list box or combo box, the lines of text in a memo field, the list of fonts supported by the screen, the names of the tabs on a notepad, the items in an outline, and a row or column of entries in a string grid.

Although applications use these lists in various ways, Delphi provides a common interface to all of them through an object called a string list, and goes even farther by making them interchangeable, meaning you can, for example, edit a string list in a memo field and then use it as the list of items in a list box.

You have probably already used string lists through the Object Inspector. A string-list property appears in the Object Inspector with [TStrings] in the Value column. When you double-click [TStrings], you get the String List editor, where you can edit, add, or delete lines.

You can also work with string lists at run time. These are the most common types of string-list tasks:

Manipulating the strings in a list

Loading and saving string lists

Creating a new string list

Adding objects to a string list

Operating on objects in a string list


See also

Creating an owner-draw control


Manipulating the strings in a list
Manipulating strings in a list

See also

Quite often in a Delphi application, you need to write code to work with strings in an existing string list. The most common case is that some component in the application has a string-list property, and you need to change it or get strings from it.

These are the common operations you might need to perform:

Counting the strings in a list

Accessing a particular string

Finding the position of a string

Adding a string

Moving a string

Deleting a string

Copying a complete string list

Iterating the strings in a list


See also

Loading and saving string lists

Creating a new string list

Adding objects to a string list

Operating on objects in a string list


Counting the strings in a list

See also	Example

To find out how many strings are in a string list, use the Count property.

Count is a read-only property that indicates the number of strings in the list. Since the indexes used in string lists are zero-based, Count is one more than the index of the last string in the list.


See also

Accessing a particular string

Finding the position of a string

Adding a string

Moving a string

Deleting a string

Copying a complete string list

Iterating the strings in a list


Example

The following example returns the number of different fonts the current screen supports.

FontCount := Screen.Fonts.Count;


Accessing a particular string

See also	Example

Each string list has an indexed Strings, property which you can treat like an array of strings. For example, the first string in the list is Strings[0].

Since the Strings property is the most common part of a string list to access, Strings is the default property of the list, meaning that you can omit the Strings identifier and just treat the string list itself as an indexed array of strings.

To access a particular string in a string list,

Refer to it by its ordinal position, or index, in the list.

The string numbers are zero-based, so if a list has three strings in it, the indexes cover the range 0..2.

To determine the maximum index,

Check the Count property.

If you try to access a string outside the range of valid indexes, the string list raises an exception.


See also

Counting the strings in a list

Finding the position of a string

Adding a string

Moving a string

Deleting a string

Copying a complete string list

Iterating the strings in a list


Example

The following examples both set the first line of text in a memo field to be "This is the first line."

Memo1.Lines.Strings[0] := 'This is the first line.';

Memo1.Lines[0] := 'This is the first line.';


Finding the position of a string within a list

See also	Example

If you have a list of strings, you can easily determine its position in a string list, or whether it is even in the list.

To locate a string in a string list,

Use the string list's IndexOf method.

IndexOf takes a string as its parameter, and returns the index of the matching string in the list, or -1 if the string is not in the list.

Note:	IndexOf works only with complete strings. That is, it must find an exact match for the whole string passed to it, and it must match a complete string in the list. If you want to match partial strings (for instance, to see if any of the strings in the list contains a given series of characters), you need to iterate the list yourself and compare the strings.


See also

Counting the strings in a list

Accessing a particular string

Adding a string

Moving a string

Deleting a string

Copying a complete string list

Iterating the strings in a list


Example

The following example uses IndexOf to determine whether a given file name is in the list of files in a file list box:

if FileListBox1.IndexOf('AUTOEXEC.BAT') > -1 then { you're in the root directory };


Adding a string to an existing list

See also	Example

There are two ways to add a string to a string list:

Add it to the end of the list

Insert it in the middle of the list

To add a string to the end of the list,

Call the Add method, passing the new string as the parameter. The added string becomes the last string in the list.

To insert a string into the list,

Call the Insert method, passing two parameters: The index where you want the inserted string to appear, and the string.

If the list does not already have at least two strings, you will get an index-out-of-range exception.


See also

Counting the strings in a list

Accessing a particular string

Finding the position of a string

Moving a string

Deleting a string

Copying a complete string list

Iterating the strings in a list


Example

The following example inserts the string 'Three' as the third string in a list:

Insert(2, 'Three');

Note:	If the list doesn't already have at least two strings, Delphi raises an index-out-of-range exception.


Moving a string within a list

See also	Example

You can move a string to a different position in a string list, such as when you want to sort the list. If the string has an associated object, the object moves with the string.

To move a string in the list,

Call the Move method, passing two parameters: the current index of the item and the index where you want to move the item to.


See also

Counting the strings in a list

Accessing a particular string

Finding the position of a string

Adding a string

Deleting a string

Copying a complete string list

Iterating the strings in a list


Example

The following example moves the third string in a list to the fifth position.

Move(3, 5);


Deleting a string from a list

See also	Example

When you have an existing list of strings, you might often want to remove a string from that list.

To delete a string from a string list,

Call the string list's Delete method, passing the index of the string you want to delete.

If you do not know the index of the string you want to delete, use the IndexOf method to locate it.

To delete all the strings in a string list,

Use the Clear method.


See also

Counting the strings in a list

Accessing a particular string

Finding the position of a string

Adding a string

Moving a string

Copying a complete string list

Iterating the strings in a list


Example

The following example uses IndexOf to determine the location of a string in a list, and deletes that string if present:

with ListBox1.Items do

begin

  if IndexOf('bureaucracy') > -1 then

    Delete(IndexOf('bureaucracy'));

end;


Copying a complete string list

See also	Example

Copying the strings from one list to another overwrites the strings that were originally in the destination list.

To copy a list of strings from one string list to another,

Assign the source list to the destination list.

Even if the lists are associated with different kinds of components (or no components at all), Delphi handles the copying of the list for you.

However, sometimes you want to append a new string list to an existing list.

To add a list of strings to the end of another list,

Call the AddStrings method, passing as a parameter the list of strings you want to add.


See also

Counting the strings in a list

Accessing a particular string

Finding the position of a string

Adding a string

Moving a string

Deleting a string

Iterating the strings in a list


Examples

The following example copies the items from a combo box into an outline:

Outline1.Lines := ComboBox1.Items;

The following example adds all the items from the combo box to the end of the outline:

Outline1.AddStrings(ComboBox1.Items);


Iterating the strings in a list

See also	Example

Many times you need to perform an operation on each string in a list, such as searching for a particular substring or changing the case of each string.

To iterate through each string in a list,

Use a for loop with an Integer-type index. Inside the loop you can access each string and perform the desired operation.

The loop should run from zero up to one less than the number of strings in the list (Count - 1).


See also

Counting the strings in a list

Accessing a particular string

Finding the position of a string

Adding a string

Moving a string

Deleting a string

Copying a complete string list


Example

The following example iterates the strings in a list box and converts each one to all uppercase characters in response to a button click:

procedure TForm1.Button1Click(Sender: TObject);

var

  Index: Integer;

begin

  for Index := 0 to ListBox1.Items.Count - 1 do

    ListBox1.Items[Index] := UpperCase(ListBox1.Items[Index]);

end;


Loading and saving string lists

See also	Example

You can easily store any Delphi string list in a text file and load it back again (or load it into a different list).

You can also use the same mechanism to save lists of items for list boxes or complete outlines.

To load a string list from a file,

Call the LoadFromFile method and pass the name of the text file to load from.

LoadFromFile reads each line from the text file into a string in the list.

To store a string list in a text file,

Call the SaveToFile method and pass it the name of the text file to save to.

If the file does not already exist, SaveToFile creates it. Otherwise, it overwrites the current contents of the file with the strings from the string list.


See also

Manipulating the strings in a list

Creating a new string list

Adding objects to a string list

Operating on objects in a string list


Example

The following example loads a copy of the AUTOEXEC.BAT file from the root directory of the C drive into a memo field and makes a backup copy called AUTOEXEC.BAK:

procedure TForm1.FormCreate(Sender: TObject);

var

  FileName: string;	{ storage for file name }

begin

  FileName := 'C:\AUTOEXEC.BAT';	{ set the file name }

  with Memo1 do

  begin

    LoadFromFile(FileName);	{ load from file }

    SaveToFile(ChangeFileExt(FileName, 'BAK'));	{ save into backup file }

  end;

end;


Creating a new string list

See also

Most of the time when you use a string list, you use one that is part of a component, so you do not have to construct the list yourself. However, you can also create standalone string lists that have no associated component. For instance, your application might need to keep a list of strings for a lookup table.

When you create your own string list you must remember to free the list when you finish with it. There are two distinct cases you might need to handle:

A list that the application creates, uses, and destroys all in a single routine

A list that the application creates, uses throughout run time, and destroys before it shuts down

The way you create and manage a string list depends on the string list type. It can be either of the following:

Long-term string lists

Short-term string lists


See also

Manipulating the strings in a list

Loading and saving string lists

Adding objects to a string list

Operating on objects in a string list


Short-term string lists

See also	Example

Short-term string lists are useful if you need to use a string list only for the duration of a single routine. You can create it, use it, and destroy it all in one place. This is the safest way to use string list objects.

Because the string list object allocates memory for itself and its strings, it is important that you protect the allocation by using a try..finally block to ensure that the object frees its memory even if an exception occurs.

The basic outline of the use of a short term string list, then, is to

1.	Construct the string-list object.

2.	In the try part of a try..finally block, use the string list.

3.	In the finally part, free the string-list object.


See also

Long-term string lists


Example

The following event handler responds to a click on a button by constructing a string list, using it, and then destroying it again.

procedure TForm1.Button1Click(Sender: TObject);

var
  TempList: TStrings;	{ declare the list }

begin

  TempList := TStringList.Create;	{ construct the list object }

  try
    { use the string list }

  finally
    TempList.Free;	{ destroy the list object }

  end;

end;


Long-term string lists

See also	Example

Long-term string lists are useful when you need a string list that is available at any time while your application runs. You need to construct the list when the application is first executed, then destroy it before the application terminates.

To create a string list that is available throughout run time,

1.	Add a field of type TStrings to the application's main form object, giving it the name you want to use.

2.	Create a handler for the main form's OnCreate event. The create-event handler is executed before the form appears onscreen at run time.

3.	In the create-event handler, construct the string-list object.

4.	Create a handler for the main form's OnDestroy event. The destroy-event handler is executed just after the main form disappears from the screen before the application stops running.

Any other event handlers can then access the string list by the name you declared in the first step.


See also

Short-term string lists


Example

The following example adds a string to the list named ClickList. The click-event handler for the main form adds a string to the list every time the user presses a mouse button, and the application writes the list out to a file before destroying the list.

unit Unit1;

interface

uses WinTYpes, WinProcs, Classes, Graphics, Forms, Controls, Apps;

type

  TForm1 = class(TForm)

    procedure FormCreate(Sender: TObject);

    procedure FormDestroy(Sender: TObject);

    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;

      Shift: TShiftState; X, Y: Integer);

  private

    { Private declarations }

  public

    { Public declarations }

    ClickList: TStrings;	{declare the field}

  end;

var

  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);

begin

  ClickList := TStringList.Create;	{construct the list}

end;

procedure TForm1.FormDestroy(Sender: TObject);

begin

  ClickList.SaveToFile(ChangeFileExt(Application.ExeName, '.LOG'));

{save the list}

  ClickList.Free;	{destroy the list object}

end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;

  Shift: TShiftState; X, Y: Integer);

begin

  ClickList.Add(Format('Click at (%d, %d)', [X, Y]));	{add a

string to the list}

end;

end.


Adding objects to a string list

See also	Example

In addition to its list of strings, stored in the Strings property, a string list can also have a list of objects, which it stores in its Objects property. Like Strings, Objects is an indexed property, but instead of an indexed list of strings, it is an indexed list of objects.

If you are just using the strings in the list, it does not matter whether you have objects: The list does nothing with the objects unless you specifically access them. Also, it does not matter what kind of object you assign to the Objects property. Delphi just holds the information; you manipulate it as you need to.

Note:	Some string lists do not allow objects, because it does not make sense to have them. For example, the string lists representing the lines in a memo or the pages in a notebook cannot have associated objects.

To associate an object with an existing string,

Assign the object to the Objects property at the same index.

Although you can assign any type of objects you want to Objects, the most common use is to associate bitmaps with strings for owner-draw controls. The important thing to remember is that strings and objects in a string list work in pairs. For every string, there is an associated object, although by default, that object is nil.

It is also important to understand that the string list does not own the objects associated with it. That is, destroying the string-list object does not destroy the objects associated with the strings.


See also

Creating an owner-draw control

Operating on objects in a string list


Operating on objects in a string list

See also

For every operation you can perform on a string list with a string, you can perform a corresponding operation with a string and its associated object. For example, you can access a particular object by indexing into the Objects property, just as you would the Strings property. The biggest difference is that you cannot omit the name Objects, since Strings is the default property of the string list.

The following table summarizes the properties and methods you use to perform corresponding operations on strings and objects in a string list.

Operation	For strings	For objects

Access an item	Strings property	Objects property

Add an item	Add method	AddObject method

Insert an item	Insert method	InsertObject method

Locate an item	IndexOf method	IndexOfObject method

Methods such as Delete, Clear and Move operate on items as a whole. That is, deleting an item deletes both the string and the corresponding object. Also note that the LoadFromFile and SaveToFile methods operate on only the strings, since they work with text files.

Accessing associated objects

You access objects associated with a string list just as you access the strings in the list. For example, to get the first string in a string list, you access the string list's Strings property at index 0: Strings[0]. The object corresponding to that string is Objects[0].


See also

Creating an owner-draw control

Adding objects to a string list


Example

The following example associates a bitmap called AppleBitmap with the string 'apple' in the string list named Fruits.

with Fruits do Objects[IndexOf('apple')] := AppleBitmap;

You can also add objects at the same time you add the strings, by calling the string list's AddObject method instead of Add, which just adds a string. AddObject takes two parameters, the string and the object. For example,

Fruits.AddObject('apple', AppleBitmap);


Viewing pages in the Code Editor

See also

When a page of the Code Editor is displayed, you can scroll through all the data it contains, not just particular sections of your code.

To view a page in the Code Editor, choose one of the following methods:

Click the tab for the page you want to view.

Press Ctrl+Tab to go forward through the Editor pages, and Shift+Ctrl+Tab to go backward.

Select a unit from the View Unit dialog box. To open the View Unit dialog box, choose View|Unit.

When the Code Editor is displayed, you can return to a form at any time using either of these methods:

To return to the form,

Click any part of the form that is visible under the Code Editor.

Choose View|Form to open the View Form dialog box, and choose the form you want to view.

Use SpeedBar buttons to display the current form, or to open the View Form dialog box.


See also

Code Editor

Locating an event handler that's not the default

Deleting event handlers


Using the Sender parameter

See also	Example

The Sender parameter in an event handler informs Delphi which component received the event, and therefore called the handler. You can write a single event handler that responds to multiple component events by using the Sender parameter in an if..then..else statement.


See also

Associating an event with an existing event handler

Displaying and coding shared events

Displaying and coding shared component events


Example

The following code either displays the title of the application in the caption of a modal dialog box, or else displays nothing, depending on whether the OnClick event was received by Button1 or by another button using the same event handler.

procedure TMain1.Button1Click(Sender: TObject);

begin

if Sender = Button1 then

  AboutBox.Caption := 'About ' + Application.Title

else AboutBox.Caption := ‘’;

AboutBox.ShowModal;

end;


Displaying shared events
Displaying and coding shared events

See also

There are many events, such as the OnClick event, that are available to more than one component. When components have events in common, you can associate the common event with an event handler (existing or new) without having to do this separately for each component.

You do this by first displaying the shared event, and then creating an event handler for it.

To display shared events,

1.	In the form, select all the components whose common events you want to view.

2.	Display the Events page of the Object Inspector.

The Object Inspector displays only those events that pertain to all the selected components. (Note also that only events in the current form are displayed.)

When you create a shared event handler (or when you reuse an existing one), Delphi does not duplicate the event handler code for every component event associated with it. You will see the code in the Code Editor only once, but the same code gets called whenever any of the component events occurs.

To associate a shared component event with an existing event handler,

1.	Select the components for which you want to associate a shared event handler.

2.	Display the Events page of the Object Inspector, and select an event.

The Object Inspector displays only those events that the selected components have in common.

3.	From the drop-down list next to the event, select an existing event handler, and press Enter.

Whenever any of the components you selected receives the specified event, the event handler you selected in called.

To create an event handler for a shared event,

1.	Select the components for which you want to create a shared event handler.

2.	Display the Events page of the Object Inspector, and select an event.

3.	Type a name for the new handler, and press Enter, or double-click the Handler column if you want Delphi to generate a name.

Delphi creates an event handler in the Code Editor, positioning the cursor in the begin..end block.

If you choose not to name the event handler, Delphi names it for you based on the order in which you selected the components.

4.	Type the code you want executed when the selected event occurs for any of the components.


See also

Associating an event with an existing event handler

Generating the default event handler

Displaying and coding shared events

Locating an event handler that's not the default

Modifying a shared event handler

Deleting event handlers


Modifying the form's type declaration

See also

When you add a component to a form, Delphi generates an instance variable, or field, for the component and adds it to the form's type declaration. Here is how adding a button changes the form's type declaration:

type

   TForm1 = class(TForm)

     Button1: TButton;	{this is the code Delphi adds}

   end;

Similarly, when you delete a component, Delphi removes the corresponding type declaration. You can view similar code being added or removed from the Code Editor.

To view code being added in the Code Editor,

1.	Click the form's Title bar and hold down the mouse button while you drag the form to a new location so you can see the entire Code Editor.

2.	Scroll in the Code Editor until the type declaration section is visible.

3.	Add a component to the form while watching what happens in the Code Editor.

4.	Delete the component, again while viewing the Code Editor.

Note:	Delphi does not remove any event handlers (or methods) associated with components you delete, because those event handlers might be called by other components in the form. You can still run your program so long as the method declaration and the method itself both remain in the unit file. If you delete the method without deleting its declaration, Delphi generates an “Undefined forward” error message.


See also

The Name property

Adding components to a form


Making a dialog box modal or modeless

See also

Because dialog boxes are simply customized forms, they, like forms, can be either modal or modeless. Most dialog boxes are modal. When a form is modal, the user must explicitly close it before working in another form. When a form is modeless, it can remain onscreen while the user works in another form.

Any form you create can be used in your application modally, or modelessly.

To display a form in a modeless state,

Call its Show method.

Note:	If you want a modeless dialog box to remain on top of other windows at run time, set its FormStyle property to fsStayOnTop.

To display a form modally,

Call its ShowModal method.


See also

Setting component properties

Building dialog boxes

Executing button code on Esc

Executing button code on Enter


Setting the tab order

See also

Tab order is the order in which focus moves from component to component in a running application when the Tab key is pressed.

To enable the Tab key to shift focus to a component on a form,

Set the TabStop property of the component to True.

The tab order is initially set by Delphi, corresponding to the order in which you add components to the form. You can change this by changing the TabOrder property of each component, or by using the Edit Tab Order dialog box.

To use the Edit Tab Order dialog box,

1.	Select the form, or a container component in the form, that contains the components whose tab order you want to set.

2.	Choose Edit|Tab Order.

The Edit Tab Order dialog box appears, displaying a list of components ordered (first to last) in their current Tab order.

3.	In the Controls list, select a component and press the up or down arrow, or drag the component to its new location in the tab order list.

4.	When the components are ordered to your satisfaction, choose OK.

Using the Edit Tab Order dialog box changes the value of the components' TabOrder property. You can also do this manually, if you want.

To remove a component from the tab order,

Set the component's TabStop property to False.

When the user presses the Tab key in the running application, the focus will skip over this component and go to the next one in the tab order. This is true even if the component has a valid TabOrder value.

Note	Removing a component from the tab order does not disable the component.

To manually change a component's TabOrder property,

1.	Select the component whose position in the tab order you want to change.

2.	In the Object Inspector, select the TabOrder property.

3.	Change the TabOrder property's value to reflect the position you want the component to have in the tab order.

Note:	The first component in the tab order should have the TabOrder value of 0.

Keep in mind the following points when manually setting your tab order (if you are using the Edit Tab Order dialog box, you do not need to worry about them):

Each TabOrder property value must be unique. If you give a component a TabOrder value that has already been assigned to another component on this form, Delphi renumbers the TabOrder value for all other components accordingly.

If you attempt to give a component a TabOrder value equal to or greater than the number of components on the form (because numbering starts with 0), Delphi does not accept the new value, instead entering a value that ensures the component will be last in the tab order.

Components that are invisible or disabled are not recognized in the tab order, even if they have a valid TabOrder value. When the user presses Tab, the focus skips over such components and goes to the next one in the tab order. For more information, see Enabling and disabling components.

Testing the Tab Order

You can test the tab order by running the application. At design time, focus always moves from component to component in the order that the components were placed on the form. Changes you make to the tab order at design time are reflected only at run time.


See also

Setting the focus in a dialog box

Enabling and disabling components

Building dialog boxes


Enabling and disabling components

See also	Example

You often want to prevent a user from accessing certain components in a dialog box or form, either initially when the dialog box opens, or in response to changing conditions with the dialog box at run time.

To disable a component at design time,

Use the Object Inspector to set the value of the Enabled property  to False.

When a component is disabled, it appears dimmed, and the user cannot tab to it, even if its TabStop property is set to True.

Note:	Certain components also contain a ReadOnly property to restrict the kind of access a user has to the contents of the component at run time.

By disabling a component at design time, you specify that the component is initially unavailable to the user when the dialog box first opens. You can also dynamically change whether a component is enabled at run time.

To disable a component at run time,

Type the following code in an event handler for the component:

<componentn>.Enabled := False;

where <componentn> is the name of the component, for example, Button1.


Example

The following event handler specifies that when the user clicks Button1, Button2 is disabled.

procedure TForm1.Button1Click(Sender: TObject);

begin

   Button2.Enabled := False

end;


See also

Setting the tab order

Developing custom dialog boxes


Setting the component focus in a form

See also

Only one component per form can be active, or have the focus, in a running application at any given time. The button with focus in a form takes the OnClick event when the Enter key is pressed.

The component having initial focus in the form at run time corresponds to the ActiveControl property of the form.

If no component is specified as the active control, Delphi gives initial focus to any button component whose Default property is set to True.

If no button is specified as the default for the form, Delphi gives initial focus to the component that is first in the Tab Order, excluding:

Disabled components

Components that are invisible at run time

Components whose TabStop property is set to False

To specify the active component at design time,

Select the form's ActiveControl property and use the drop-down list to select the the component you want to have focus when the form first opens.

To change the active component during run time,

Call the SetFocus method from an event handler, for example,

<componentn>.SetFocus;

where <componentn> is the name of the component, for example, Button1.

Note:	If you set a button as the active component for the form at design time, that setting overrides, at run time, any default button you might have specified.


See also

Setting the tab order

Developing custom dialog boxes


Providing command buttons

See also

Depending on whether you intend to use your dialog box in a modal or modeless state, you might want to provide certain command buttons in the dialog box. For modal dialog boxes, you need to provide the user with a way to exit the dialog box. It’s fairly standard design to provide one or more command buttons for this purpose. For simple modal dialog boxes, such as a message box, one button is often sufficient. Such a button might be labeled, for instance, “OK” or “Close.” (If you have another button in the dialog box labeled “No,” as described in the next paragraph, this button might be labeled “Yes.”)

In cases where the dialog box accepts input from the user, you want to provide users with a choice of whether or not to process their input on exiting the dialog box. You can do this by means of an additional button labeled, for example, “Cancel” or “No.” (If your dialog box explicitly asks a question of the user, you might want to label this button “No”; otherwise, “Cancel” is usually more appropriate.)

Your code controls what happens when a user chooses a command button, for example, whether changes are processed or not.

By setting properties of the Button component, you can call a button’s event-handler code when the user presses Enter or Esc; and you can specify that the dialog box close when the user chooses a command button, without writing any additional code. See the following topics for more information:

Executing button code on Esc

Executing button code on Enter

Note:	You can quickly create many standard command buttons by adding a BitBtn component to the form and setting its Kind property.


See also

Setting the tab order

Setting the focus in a dialog box

Building dialog boxes


Executing button code on Esc

See also

Delphi provides a Cancel property for Button components. When your form contains a button whose Cancel property is set to True, pressing the Esc key at run time executes any code contained in the button’s OnClick event handler.

To designate a button as the Cancel button,

Set its Cancel property to True.

To specify that the modal dialog box close when the user chooses a Cancel button, set the button's ModalResult property to mrCancel.

Setting a button's ModalResult property to a nonzero value means the modal dialog box closes automatically when the user chooses the button.

You can also use the BitBtn component to create a Cancel button.

To use the bitmap button to create a Cancel button,

Add a BitBtn component to your form, and set its Kind property to bkCancel. This sets the button's Cancel property to True, and the ModalResult property to mrCancel.


See also

Executing button code on Enter


Executing button code on Enter

See also

When your form contains a button whose Default property is set to True, pressing Enter at run time executes any code contained in the button’s OnClick event handler--unless another button has focus when the Enter key is pressed.

Even if your form contains a default button, another button can take focus away at run time. Pressing the Enter key calls the OnClick event handler code of the button with focus, overriding any other button’s Default property setting. (The button with focus is indicated by a darker, thicker border than that of other buttons in the dialog box.)

Note:	Although other components in a form can have focus, only button components respond when the user presses Enter. The default button takes the OnClick event when another non-button component in the form has focus.

To specify a button as the default button,

Set its Default property to True.

To specify that the modal dialog box close when the user chooses a default button, set the button's ModalResult property to mrOK.

Setting a button's ModalResult property to a nonzero value means the modal dialog box closes automatically when the user chooses the button.

You can also use the BitBtn component to create a Default button.

To use the bitmap button to create a default button,

Add a BitBtn component to your form, and set its Kind property to bkOK. This automatically sets the button's Default property to True and the ModalResult property to mrOK.

To change focus at run time,

Call the button’s SetFocus method.


See also

Executing button code on Esc


Setting form properties for a dialog box

See also

By default, Delphi forms have Maximize and Minimize buttons, a resizable border, and a Control menu that provides additional commands to resize the form. While these features are useful at run time for modeless forms, modal dialog boxes seldom need them.

Delphi provides a BorderStyle property for the form that includes several useful values. Setting the form's BorderStyle to bsDialog implements the most common settings for a dialog box, such as:

Removing the Minimize and Maximize buttons

Providing a Control menu with only the Move and Close options

Making the form border non-resizeable, and giving it a "beveled" appearance

The following table shows other form property settings that can be used, individually or in concert, to create different form styles.

Property	Setting	Effect

BorderIcons

    biSystemMenu	False	Removes Control (System) menu

    biMinimize	False	Removes Minimize button

    biMazimize	False	Removes Maximize button

BorderStyle	bsSizeable	Enables the user to resize the form border

bsSingle	Provides a single outline, non-resizeable border

bsNone	No distinguishable border; not resizeable

bsDialog	Window has a border but not resizeable

bsToolWindow	Makes title bar small; window is not resizeable

bsSizeToolWindow	Makes title bar small; window is resizeable

Note:	Changing these settings does not change the design-time appearance of the form; these property settings become visible at run time.

When you remove the form's Control (or system) menu, you need to provide the user with a way to exit the dialog box. You can do this by including buttons in the form.

Specifying a caption for a dialog box

In most Windows-based applications, each dialog box has a caption on its Title bar that describes the primary function of the dialog box.

By default, Delphi displays the Name property value for each form in the form's Title bar. If you change the Name property of the form prior to changing the Caption property, the Title bar caption changes to the new name. Once you change the Caption property, the form's title bar always reflects the current value of Caption.


See also

Developing custom dialog boxes


Testing the user interface

See also

After you have spent some time designing forms and coding event handlers, you might want to test your user interface to see whether it responds as you want. For instance, you can check that the proper component has focus as the application begins running, that the tab order is correct, and so on. You do not need to have a fully functional application to test your work.

To run your application, choose one of the following methods:

Choose Run|Run.

Click the Run button on the SpeedBar.

This compiles and then executes your program.

To terminate your application, choose one of the following methods:

Double-click the form's Control-menu box.

Choose Run|Program Reset.


See also

Component basics

Compiling, building, and running projects


Coding the Window menu commands

See also

MDI applications should always include a Window (or other) menu item that contains Tile, Cascade, and Arrange Icons commands to offer users an easy way to arrange their open documents in the client area of the frame window.

To handle the clicks for the Tile, Cascade, and Arrange Icons menu commands, generate OnClick event handlers for each menu item, and call the Tile, Cascade, or ArrangeIcons method as appropriate.  For each event handler, you need write only one line of code--a method call--and Delphi does the rest for you.

For example:

procedure TFrameForm.Tile1Click(Sender: TObject);

begin

  Tile;

end;

procedure TFrameForm.Cascade1Click(Sender: TObject);

begin

  Cascade;

end;

procedure TFrameForm.ArrangeIcons1Click(Sender: TObject);

begin

  ArrangeIcons;

end;


See also

Including a list of open documents in a menu

Arranging open child windows

Creating an MDI application


Displaying a list of open documents in a menu
Including a list of open documents in a menu

See also

MDI applications should always include a menu item that contains a list of the open document windows, which lets users quickly switch among them. (The window that currently has focus appears in the list with a check mark next to it.)

You can add a list of open documents to any menu item that appears on a menu bar in the MDI form. This list can, but need not, be included on the Window menu -- for example, it could be on a File or View menu. However, there can be only one such list per menu bar. The list of open documents appears below the last item in the menu.

To include a list of open documents as part of a menu, set the frame form's WindowMenu property to the name (not the caption) of the menu under which you want the list to appear.

To include an open document list in a menu,

1.	Set the form style to fsMDIForm.

This makes the form an MDI frame.

2.	Create a menu for the MDI form that contains the menu item where you want the open document list to appear.

3.	Select the frame form, and then select the Properties page of the Object Inspector.

4.	From the drop-down list next to the WindowMenu property, select the name of the menu item under which you want the open document list to appear (for example, a Window or View menu.

This name must represent an item that appears on the menu bar, not a submenu item, because document lists cannot be used in nested menus.


See also

Coding the window menu commands

Arranging open child windows

Creating an MDI application

Designing menus


Adding a separator bar to a menu

Enter a hyphen as the caption of the menu item.


Specifying keyboard shortcuts

See also

Enter a value for the ShortCut property, or or select a key combination from the drop-down list. However, this list is only a subset of the valid combinations you can type in.

Keyboard shortcuts enable the user to perform the action without accessing the menu directly by typing the shortcut key combination.

Caution:	Delphi does not check for duplicate shortcut keys, you must track values you have entered in your application menus.


See also

Adding, inserting, and deleting menu items

Adding, inserting, and deleting menu items and keyboard shortcuts


Editing menu items without opening the Menu Designer

See also

When you edit a menu item by using the Menu Designer, its properties are still displayed in the Object Inspector. You can switch focus to the Object Inspector and continue editing the menu item properties there. Or you can select the menu item from the Component list and edit its properties without ever opening the Menu Designer.

To edit a menu item without opening the Menu Designer,

Select the item from the Component list.

To close the Menu Designer window and continue editing menu items,

1.	Switch focus from the Menu Designer window to the Object Inspector by clicking the properties page of the Object Inspector.

2.	Close the Menu Designer as you normally would.

The focus remains in the Object Inspector, where you can continue editing properties for the selected menu item.

To edit another menu item, select it from the Component list.


See also

Designing menus

Menu Designer SpeedMenu


Menu Designer SpeedMenu

See also

The Menu Designer SpeedMenu provides quick access to the most common Menu Designer commands, and to the menu template options.

To display the Menu Designer SpeedMenu,

Choose one of the following methods:

Right-click anywhere on the Menu Designer.

Press Alt+F10 when the cursor is in the Menu Designer window.

The first three commands on the Menu Designer SpeedMenu directly perform an action.

Command	Action

Insert	Inserts a placeholder above or to the left of the cursor

Delete	Deletes the selected menu item (and all its sub-items, if any)

Create SubMenu	Creates a placeholder at a nested level and adds an arrow to the right of the selected menu item

The rest of the commands on the Menu Designer SpeedMenu open dialog boxes. Choose a command for more information.

Select Menu

Save As Template

Insert From Template

Delete Templates

Insert from Resource


See also

Designing menus

Setting menu item properties by using the Object Inspector

Switching among menus at design time


Insert (Menu Designer SpeedMenu)

Choose Insert from the Menu Designer SpeedMenu to add a menu item placeholder before the selected menu item.


Delete (Menu Designer SpeedMenu)

Choose Delete from the Menu Designer SpeedMenu to remove the selected menu item.


Create Submenu (Menu Designer SpeedMenu)

Choose Create Submenu from the Menu Designer SpeedMenu to insert a menu item placeholder to the right of the selected menu item and add an arrow to the selected item to indicate a nested level.


Select Menu (Menu Designer SpeedMenu)

See also

Choose Select Menu from the Menu Designer SpeedMenu to open the Select Menu dialog box.

Select Menu dialog box

Use this dialog box to quickly select from among the existing form menus.


See also

Switching among menus at design time


Save As Template (Menu Designer SpeedMenu)

See also

Choose Save As Template from the Menu Designer SpeedMenu to open the Save Template dialog box, which enables you to save a menu for later reuse.

Save Template dialog box

Use this dialog box to save a menu for reuse.


See also

Saving a menu as a template


Insert From Template (Menu Designer SpeedMenu)

See also

Choose Insert From Template from the Menu Designer SpeedMenu to open the Insert Template dialog box.

Insert Template Dialog Box

Use this dialog box to add a predesigned menu to the active menu component.


See also

Using menu templates


Delete Templates (Menu Designer SpeedMenu)

See also

Choose Delete Templates from the Menu Designer SpeedMenu to open the Delete Templates dialog box.

Delete Templates dialog box

Use this dialog box to select and remove a predesigned menu.

Note:	After you delete a template, you cannot retrieve it.


See also

Using menu templates


Insert from Resource (Menu Designer SpeedMenu)

See also

Choose Insert From Resource from the Menu Designer SpeedMenu to open the Insert Menu From Resource dialog box.

Insert Menu From Resource dialog box

Use this dialog box to import a menu from a Windows resource (.RC) file. You first need to save each individual menu as a separate resource file.

Dialog box options

File Name

Enter the name of the file you want to use, or enter wildcards to use as filters in the Files list box.

Files

Displays the files in the current directory that match the wildcards in the File Name edit box or the file type in the List Files Of Type combo box.

List Files Of Type

Choose the type of file you want to open; the default file type is a menu file (.MNU). All files in the current directory of the selected type appear in the Files list box.

Directories

Select the directory whose contents you want to view. In the current directory, files that match the wildcards in the File Name input box or the file type in the List Files of Type combo box appear in the Files list box.

Drives

Select the current drive. The directory structure for the current drive appears in the Directories list box.


See also

Importing menus from resource files


Switching amoung menus at design time
Switching among menus at design time

See also

If you are designing several menus for your form, you can use the Menu Designer SpeedMenu or the Object Inspector to easily select and move among them.

To use the SpeedMenu to switch among menus in a form,

1.	Right-click in the Menu Designer to display the SpeedMenu.

2.	From the SpeedMenu, choose Select Menu.

The Select Menu dialog box appears. This dialog box lists all the menus associated with the form whose menu is currently open in the Menu Designer.

3.	From the list in the Select Menu dialog box, choose the menu you want to view or edit.

To use the Object Inspector to switch among menus in a form,

1.	Give focus to the form whose menus you want to choose from.

2.	From the Component list, select the menu you want to edit.

3.	On the Properties page of the Object Inspector, select the Items property for this menu, and then either click the ellipsis button (...), or double-click [Menu].


See also

Menu Designer SpeedMenu


Using menu templates

See also

Delphi provides several predesigned menus, or menu templates, that contain frequently used commands. You can use these menus in your applications without modifying them (except to write code), or you can use them as a starting point, customizing them as you would a menu you originally designed yourself. Menu templates do not contain any event handler code.

Menu templates are stored in the file DELPHI.DMT. The menu templates shipped with Delphi also reside in this file. In a default installation, this file is in the \DELPHI\BIN directory. If you want to store the DELPHI.DMT file in a different directory, add the following lines to your WINDOWS\DELPHI.INI file, replacing "directory" with a directory you choose:

[Globals]

PrivateDir=directory

You can also save as a template any menu that you design using the Menu Designer. After saving a menu as a template, you can use it as you would any predesigned menu. If you decide you no longer want a particular menu template, you can delete it from the list.

To add a menu template to your application,

1.	Right-click the Menu Designer window.

The Menu Designer SpeedMenu appears.

2.	From the SpeedMenu, choose Insert From Template.

(If there are no templates, the Insert From Template option appears dimmed in the SpeedMenu.)

The Insert Template dialog box opens, displaying a list of available menu templates.

3.	Select the menu template you want to insert, then press Enter or choose OK.

This inserts the menu into your form at the cursor's location. For example, if your cursor is on a menu item in a list, the menu template is inserted above the selected item. If your cursor is on the menu bar, the menu template is inserted to the left of the cursor.

To delete a menu template,

1.	Right-click the Menu Designer window.

The Menu Designer SpeedMenu appears.

2.	From the SpeedMenu, choose Delete Templates.

(If there are no templates, the Delete Templates option appears dimmed in the SpeedMenu.)

The Delete Templates dialog box opens, displaying a list of available templates.

3.	Select the menu template you want to delete, and press Del.

Delphi deletes the template from the templates list and from your hard disk.


See also

Designing menus

Saving a menu as a template


Saving a menu as a template

See also

Any menu you design can be saved as a template so you can use it again. You can use menu templates to provide a consistent look to your applications, or use them as a starting point which you then further customize.

The menu templates you save are stored in the DELPHI.DMT file in your \DELPHI\BIN directory.

You edit the template file by using the template commands from the Menu Designer SpeedMenu.

To save a menu as a template,

1.	Choose Save As Template from the Menu Designer SpeedMenu to open the Save Template dialog box.

2.	In the Template Description edit box, enter a brief description for this menu.

3.	Click OK.

The Save Template dialog box closes, saving your menu design and returning you to the Menu Designer window.

Note:	The description you enter is displayed only in the Save Template, Insert Template, and Delete Templates dialog boxes. It is not related to the Name or Caption property for the menu.

When you save a menu as a template, Delphi does not save its Name, since every menu must have a unique name within the scope of its owner (the form). However, when you insert the menu as a template into a new form by using the Menu Designer, Delphi then generates new names for it and all its items.

Delphi also does not save any event handlers associated with a menu saved as a template, since Delphi cannot test whether the code would be applicable in a new form. You can associate menu items in the template with existing event handlers in the form.


See also

Associating menu events with code

Using menu templates


Importing menus from resource files

See also

Delphi supports menus built with other applications, so long as they are in the standard Windows resource (.RC) file format. You can import such menus directly into your Delphi project, saving you the time and effort of rebuilding menus that you created elsewhere.

To load an existing .RC menu file,

1.	In the Menu Designer, place the cursor where you want the menu to appear.

The imported menu can be part of a menu you are designing, or an entire menu in itself.

2.	From the Menu Designer SpeedMenu, choose Insert From Resource.

The Insert Menu From Resource dialog box appears.

3.	In the dialog box, select the resource file you want to load,

4.	Choose OK.

Note:	If your resource file contains more than one menu, you first need to save each menu as a separate resource file before importing it.


See also

Designing menus


Using the Object Repository

See also

Delphi's Object Repository provides a versatile mechanism for sharing forms, dialog boxes, and data modules across projects. It can also help with reusing similar forms in a single project, and provides project templates as starting points for new projects.

The following topics focuse on how to use the Object Repository in general as a project management tools and discusses some of the mechanics of using project templates.

About the Object Repository

Changing defaults for new projects

Customizing the Object Repository

Object Repository usage options

Using project templates

Using the Object Repository in a shared environment


Object RepositorySA
See also

 Object Repository dialog box


About the Object Repository

See also

Delphi provides the Object Repository as a means for sharing and reusing forms and projects. The repository itself is really just a text file that contains references to forms, projects, and experts. Details of the file format are in online Help. The Object Repository has replaced the Gallery in this version of Delphi.

Sharing across projects

By adding forms, dialog boxes, and data modules to the Object Repository, you make them available to other projects. For example, in a simple case, you could have all your projects use the same About box, copied from the Object Repository. A more advanced use of the Object Repository would be to have a standard empty dialog box with the company or product logo and standard button placement, from which all your projects derive standard-looking dialog boxes.

These sharing options are described in detail in Object Repository usage options.

Sharing within projects

The Object Repository can also help you to share items within a project, by allowing you to inherit from forms already in the project. When you open the New Items dialog box (by choosing File|New), you'll see a page tab with the name of your project. If you click that page tab, you'll see all the forms, dialog boxes, and data modules in your project. You can then derive a new item from the existing item, and customize it as needed.

For example, in a database application you might need several forms that display the same data, but which provide different command buttons. Instead of creating and maintaining several nearly-identical forms, you could lay out a generic form that contains all the data-display controls, then create separate forms that inherit the data-display layout, but have different command buttons.

By carefully planning your project forms, you can save tremendous amounts of time and effort by sharing forms within projects.

Sharing entire projects

You can also add an entire project to the Object Repository as a template for future projects. If you have a number of similar applications, for example, you can base them all on a single, standardized model.

Using experts

The Object Repository also contains references to experts, which are small applications that lead the user through a series of dialog boxes to create a form or project. Delphi provides a number of experts, and you can also add your own.


Object Repository usage options

See also

When you use an item from the Object Repository in a project you have as many as three options on how to include that item. Keep in mind that items in the Object Repository are there to be shared, and that you want to use them in ways that help, rather than hinder, reuse.

In general, you these are the three options for using Object Repository items:

Copy the item

Inherit from the item

Use the item directly

Copying items from the Object Repository

The simplest sharing option is to copy an item from the Object Repository into your project. Copying makes an exact duplicate of the item as it stands and adds the copy to your project. Future changes to the item in the Object Repository will not be reflected in your copy, and alterations made to your copy will not affect the original Object Repository item.

Note:	Copying is the only option available for using project templates.

Inherit from Object Repository items

The most flexible and powerful sharing option is to inherit from an item in the Object Repository. Inheriting derives a new class from the item and adds the new class to your project. When you recompile your project, any changes made to the item in the Object Repository will be reflected in your derived class, unless you have changed a particular aspect. Changes made to your derived class do not affect the shared item in the Object Repository.

Note:	Inheriting is available as an option for forms, dialog boxes, and data modules, but not for project templates. It is the only option available for reusing items from within the same project.

Using Object Repository items directly

The least flexible sharing option is using an item from the Object Repository directly in your project. Using the item adds the item itself to your project, just as if you had created it as part of that project. Design-time changes made to the item therefore appear in all projects that directly use the item, as well as affecting any projects that inherit from the item.

Note:	Using items directly is an available option for forms, dialog boxes, and data modules.

Items shared this way should generally be modified only at run time, to avoid making changes that affect other projects.

The Use option is the only option available for experts, whether form experts or project experts. Using an expert doesn't actually add shared code, but rather runs a process that generates its own code.


Using project templates

See also

Delphi provides project templates, pre-designed projects you can use as starting points for your own projects. Project templates are part of the Object Repository (located in the OBJREPOS subdirectory), which also provides form objects and experts.

When you start a project from a project template (other than the blank project template), Delphi prompts you for a project directory, a subdirectory in which to store the new project's files. If you specify a directory that doesn't currently exist, Delphi creates it for you. Delphi copies the template files to the project directory. You can then modify it, adding new forms and units, or use it unmodified, adding only your event-handler code. In any case, your changes affect only the open project. The original project template is unaffected and can be used again.

To start a new project from a project template,

1.	Choose File|New to display the New Items dialog box.

2.	Choose the Projects tab.

3.	Select the project template you want and choose OK.

4.	In the Select Directory dialog box, specify a directory for the new project's files.

A copy of the project template opens in the specified directory.

Adding projects to the Object Repository

You can add your own projects and forms to those already available in the Object Repository. This is helpful in situations where you want to enforce a standard framework for programming projects throughout an organization.

For example, suppose you develop custom billing applications. You might have a generic billing application project that contains the forms and features common to all billing systems. Your business centers around adding and modifying features in this application to meet specific client requirements. In such a case, you might want to save the project containing your Generic Billing application as a project template and perhaps specify it as the default new project on your Delphi development system. Likewise, you'll probably have a particular form within this project that you want to appear as the default main or new form.

To add a project to the Object Repository,

1.	If necessary, open the project you want added to the Object Repository.

2.	Choose Tools|Repository.

3.	On the Project Templates page, choose Add to display the Save Project Template dialog box.

4.	In the Title edit box, enter a project title.

 The title for the template will appear in the Object Repository window.

5.	In the Description field, enter text that describes the template.

 This text will appear in the Object Repository window's status bar.

6.	In the Page field, choose the name of the page in the New Items dialog box (probably Projects) you want the template to appear on.

7.	In the Author field, enter text identifying the author of the application.

 Author information appears only when the user views the repository items with full details.

8.	Choose Browse to select an icon to represent this template in the Object Repository.

9.	Choose OK to save the current project as a project template.

Note:	If you later make changes to a project template, those changes automatically appear in new projects created from that template. They will not, however, affect projects already created from that template.

You can also save your own forms as form templates and add them to those already available in the Object Repository. This is helpful in situations where you want to develop standard forms for an organization's software, as in the earlier example.

To add a form to the Object Repository as a template, follow the preceding steps for adding projects as templates, using the Form Templates page of the Object Repository Options dialog box instead of the Project Templates page.


Customizing the Object Repository

See also

The settings in the Object Repository Options dialog box affect the behavior of Delphi when you begin a new project or create a new form in an open project. This is where you specify

Default project

Default new form

Default main form

You always have to option to override these defaults by choosing File|New and selecting from the New Items dialog box.

By default, opening a new project displays a blank form. You can change this default behavior by changing Object Repository options.

Specifying the default new project

The default new project opens whenever you choose New Application from the File menu on the Delphi menu bar. If you haven't specified a default project, Delphi creates a blank project with an empty form. You can specify a project template (including a project you have created and saved as a template) as the default new project.

You can also designate a project expert to run by default when you start a new project. A project expert is a program that enables you to build a project based on your responses to a series of dialog boxes.

To specify the default new project,

1.	Choose Tools|Repository to display the Object Repository dialog box.

2.	Choose Projects in the Pages list.

3.	Select the project object you want as the default new project from the Objects list.

4.	With the object you want selected, check New Project.

5.	Choose OK to register the new default setting.

Specifying the default new form

The default new form opens whenever you choose File|New Form or use the Project Manager to add a new form to an open project. If you haven't specified a default form, Delphi uses a blank form. You can specify any form template, including a form you have created and saved as a template, as the default new form. Or you can designate a form expert to run by default when a new form is added to a project.

To specify the default new form for new projects,

1.	Choose Tools|Repository to display the Object Repository dialog box.

2.	Choose Forms in the Pages list.

3.	Select the form object you want as the default new form.

4.	With the object you want selected, check New Form.

5.	Choose OK to register the new default setting.

Specifying the default main form

Just as you can specify a form template or expert to be used whenever a new form is added to a project, you can also specify a form template or expert that should be used as the default main form whenever you begin a new project.

To specify the default main form for open projects,

1.	Choose Tools|Repository to display the Object Repository dialog box.

2.	Choose Forms in the Pages list.

3.	Select the form object you want as the default main form.

4.	With the object you want selected, check Main Form.

5.	Choose OK to register the new default setting.


Changing defaults for new projects

See also

The Project Options dialog box contains a check box labeled Default. This control enables you to modify some of Delphi's default project configuration properties. Checking this control writes the current settings from the Compiler, Linker, and Directories/Conditionals pages of the Project Options dialog to a file called DEFPROJ.DOF. Delphi creates this file when you check the Default box and choose OK in the Project Options dialog box. Delphi then uses the project options settings stored in this file as the default for any new projects you create.

If you create a project from a template in the Object Repository that has its own options file, those settings will override the default settings in DEFPROJ.DOF.

To restore Delphi's original default settings, delete or rename the DEFPROJ.DOF file.

Note:	Project options you set for an open project override the current Delphi defaults, whether those defaults are as originally shipped or as modified by you or another user.


Using the Object Repository in a shared environment

To change the location where Delphi looks for the Object Repository File, DELPHI32.DRO, create a new String Value called "BaseDir" in the “HKEY_CURRENT_USER | Software | Borland | Delphi | 2.0 | Repository” key in the Windows Registry Editor and set its Value data to the directory where this file is to be located.  It is recommended that the location uses the UNC name when the DELPHI32.DRO file is to be shared.

It is suggested that Forms and Projects be saved using UNC names when they will be added to a shared Repository.

While someone is modifying the Object Repository (DELPHI32.DRO file), anyone attempting to open or add to the repository will get a dialog box with the user name of the person that has the repository open.  If you are attempting to open the repository from Tools | Object Repository, the dialog box will ask if you wish to view the repository. If you choose to view the repository, you will not be allowed to save any changes.

Lock information is stored in the DELPHI32.DRL file. If this lock file cannot be opened, an exception is raised.  This can mean the file is read-only or the user doesn’t have write rights for the directory.  Also, if someone exits from Delphi abnormally while modifying the repository, the lock file may still contain information that the user is editing the repository and you will not be allowed to modify the DELPHI32.DRO file. In this case the lock file should be deleted.


<Library Name>is already loaded, probably as a result of an incorrect program termination. Your system may be unstable and you should exit and restart Windows now.

An error occurred while attempting to initialize Delphi's component library.  One or more DLLs are already in memory, probably as a result of an incorrect program termination in a previous Delphi or BDE session.

You should exit and then restart Windows.


<IDname> is not a valid identifier

The identifier name is invalid. Ensure that the first character is a letter or an underscore (_). The characters that follow must be letters, digits, or underscores, and there cannot be any spaces in the identifier.


A field or method named <Name> already exists

The name you have specified is already being used by an existing method or field.

For a complete list of all fields and methods defined, check the form declaration at the top of the unit source file.


A component class named <Name> already exists

The component library already contains a component with the same class name you have specified.


Breakpoint is set on line that contains no code or debug information. Run anyway?

A breakpoint is set on a line that does not generate code or in a module which is not part of the project.  If you choose to run anyway, invalid breakpoints will be disabled (ignored).


Could not stop due to hard mode

The integrated debugger has detected that Windows is in a modal state and will not allow the debugger to stop your application.  Windows enters "hard mode" whenever processing an inter-task SendMessage, when there is no task queue, or when the menu system is active.  You will not generally encounter hard mode unless you are debugging DDE or OLE processes within Delphi.

A standalone debugger such as the Turbo Debugger for Windows can be used to debug applications even when Windows is in hard mode.


Another file named <FileName> is already on the search path

A file with the same name as the one you just specified is already in another directory on the search path.


Cannot find <FileName.PAS> or <FileName.DCU> on the current search path

The .PAS or .DCU file you just specified cannot be found on the search path.

You can modify the search path, copy the file to a directory along the path, or remove the file from the list of installed units.


Cannot find implementation of method <MethodName>

The indicated method is declared in the form's class declaration but cannot be located in the implementation section of the unit.  It probably has been deleted, commented out, renamed, or incorrectly modified.

Use UNDO to reverse your changes, or correct the procedure declaration manually.  Be sure the declaration in the class is identical to the one in the implementation section.  (This is done automatically if you use the Object Inspector to create and rename event handlers.)

For more information about the syntax of procedure declarations, see Procedures and functions


Debug session in progress. Terminate?

Your application is running and will be terminated if you proceed.  When possible, you should cancel this dialog and terminate your application normally (for example, by selecting Close on the System Menu).


Declaration of class <ClassName> is missing or incorrect

Delphi is unable to locate the form's class declaration in the interface section of the unit.  This is probably because the type declaration containing the class has been deleted, commented out, or incorrectly modified.  This error will occur if Delphi cannot locate a class declaration equivalent to the following:

type

...

TForm1 = class(TForm)

...

Use UNDO to reverse your edits, or correct the declaration manually.  For more information about class declaration syntax, see Class Types.


Error address not found

The address you have specified cannot be mapped to a source code position.  This error usually occurs for one of the following reasons:

The address you entered is invalid or is not an address in your application.

The module containing the specified address was not compiled with debug information.

The address specified does not correspond to a program statement.

Note that the runtime and visual component libraries are compiled without debug information.


Error creating process: <Process> (<ErrorCode>)

Delphi was unable to start your application for the reason specified.

For more information about "Insufficient memory to run" errors, see README.TXT.


Field <Field Name> does not have a corresponding component. Remove the declaration?

The first section of your form's class declaration defines a field for which there is no corresponding component on the form.  Note that this section is reserved for use by the form designer.

To declare your own fields and methods, place them in a separate public, private, or porotected section.

This error will also occur if you load the binary form file (.DFM) into the Code Editor and delete or rename one or more components.


Field <Field Name> should be of type <Type1> but is declared as <Type2>. Correct the declaration?

The type of specified field does not match its corresponding component on the form.  This error will occur if you change the field declaration in the Code Editor or load the binary form file (.DFM) into the Code Editor and modify the type of a component.

If you select No and run your application, an error will occur when the form is loaded.


IMPLEMENTATION part is missing or incorrect

In order to keep your form and source code synchronized, Delphi must be able to find the unit's implementation section.  This reserved word has been deleted, commented out, or misspelled.

Use UNDO to reverse your changes or correct the reserved word manually.  For more information about unit syntax, see Unit syntax.


Incorrect field declaration in class <ClassName>

In order to keep your form and source code synchronized, Delphi must be able to find and maintain the declaration of each field in the first section of the form's class definition.  Though the compiler allows more complex syntax, the form designer will report an error unless each field that is declared in this section is equivalent to the following:

type

...

TForm1 = class(TForm)

Field1:FieldType;

Field2:FieldType;

...

This error has occurred because one or more declarations in this section have been deleted, commented out, or incorrectly modified.  Use UNDO to reverse your changes or correct the declaration manually.

Note that this first section of the form's class declaration is reserved for use by the form designer.  To declare your own fields and methods, place them in a separate public, private, or protected section.


Incorrect method declaration in class <ClassName>

In order to keep your form and source code synchronized, Delphi must be able to find and maintain the declaration of each method in the first section of the form's class definition.  The form designer will report an error unless the field and method declarations in this section are equivalent to the following:

type

...

TForm1 = class(TForm)

Field1:FieldType;

Field2:FieldType;

...

<Method1 Declaration>;

<Method2 Declaration>;

...

...

This error has occurred because one or more method declarations in this section have been deleted, commented out, or incorrectly modified.  Use UNDO to reserve your changes or correct the declaration manually.

Note that this first section of the form's class declaration is reserved for use by the form designer.  To declare your own fields and methods, place them in a separate public, private, or protected section.


Insufficient memory to run

Delphi was unable to run your application due to insufficient memory or Windows resources.  Close other Windows applications and try again.

This error sometimes occurs because of insufficient low (conventional) memory.  For further information, see README.TXT.


Invalid event profile <Name>

The VBX control you are installing is invalid.


Module header is missing or incorrect

The module header has been deleted, commented out, or otherwise incorrectly modified.  Use UNDO to reverse your changes, or correct the declaration manually.

In order to keep your form and source code synchronized, Delphi must be able to find a valid module header at the beginning of the source file.  A valid module header consists of the reserved word unit, program or library, followed by an identifier (for example, Unit1, Project1), followed by a semi-colon.  The file name must match the identifier.

For example, Delphi will look for a unit named Unit1 in UNIT1.PAS, a project named Project1 in PROJECT1.DPR, and a library (.DLL) named MyDLL in MYDLL.DPR.

Note that module identifiers cannot exceed eight characters in length.


No code was generated for the current line

You are attempting to run to the cursor position, but you have specified a line that did not generate code, or is in a module which is not part of the project.

Specify another line and try again.

Note that the smart linker will remove procedures that are declared but not called by the program (unless they are virtual method of an object that is linked in).


Property and method <MethodName> are not compatible

You are assigning a method to an event property even though they have incompatible parameter lists.  Parameter lists are incompatible if the number of types of parameters are not identical.  For a list of compatible methods in this form, see the dropdown list on the Object Inspector Events page.


Source has been modified. Rebuild?

You have made changes to one or more source or form modules while your application is running.  When possible, you should terminate your application normally (select No, switch to your running application, and select Close on the System Menu), and then run or compile again.

If you select Yes, your application will be terminated and then recompiled.


Symbol <BrowseSymbol> not found.

The browser cannot find the specified symbol.  This error will occur if you enter an invalid symbol name or if debug information is not available for the module that contains the specified symbol.


The <Method Name> method referenced by <Form Name> does not exist. Remove the reference?

The indicated method is no longer present in the class declaration of the form.  This error occurs when you manually delete or rename a method in the form's class declaration that is assigned to an event property.

If you select No and run this application, an error will occur when the form is loaded.


The <Method Name> method referenced by <Form Name>.<Event Name> has an incompatible parameter list. Remove the reference?

A form has been loaded that contains an event property mapped to a method with an incompatible parameter list.  Parameter lists are incompatible if the number or types of parameters are not identical.

For a list of methods declared in this form which are compatible for this event property, use the dropdown list on the Object Inspector's Events page.

This error occurs when hyou manually modify a method declaration that is referenced by an event property.

Note that it is unsafe to run this program without removing the reference or correcting the error.


The project already contains a form or module named <Name>

Every module name (program or library, form and unit) in a project must be unique.


USES clause is missing or incorrect

In order to keep your forms and source code synchronized, Delphi must be able to find and maintain the USES clause of each module.

In a unit, a valid USES clause must be present immediately following the interface reserved word.  In a program or library, a valid USES clause must be present immediately following the program or library header.

This error occurs because the USES clause has been deleted, commented out, or incorrectly modified.  Use UNDO to reverse your changes or correct thte declaration manually.  For more information about the USES clause syntax, see the reserved word USES.


Searching on a table

You can search a table for specific records using the generic search methods Locate and Lookup. These methods enable you to search on any type of columns in any table, whether or not they are indexed or keyed.

Table components also continue to support Goto and Find methods for compatibility with previous versions of Delphi. While these methods are documented here to allow you to work with legacy applications, you should always use Lookup and Locate in your new applications. Moreover, you may see performance gains in existing applications if you convert them to use the new method.

Using Locate

Locate moves the cursor to the first row matching a specified set of search criteria. In its simplest form, you pass Locate the name of a column to search, a field value to match, and an options flag specifying whether the search is case-insensitive or if it can use partial-key matching. For example, the following code moves the cursor to the first row in the CustTable where the value in the Company column is "Professional Divers, Ltd.":

var

  LocateSuccess: Boolean;

  SearchOptions: TLocateOptions;

begin

  SearchOptions := [loPartialKey];

  LocateSuccess := CustTable.Locate('Company', 'Professional Divers, Ltd.',
    SearchOptions);

end;

If Locate finds a match, the first record containing the match becomes the current record. Locate returns True if it finds a matching record, False if it does not. If a search fails, the current record does not change.

The real power of Locate comes into play when you want to search on multiple columns and specify multiple values to search for. Search values are variants, which enables you to specify different data types in your search criteria. To specify multiple columns in a search string, separate individual items in the string with semi-colons.

Because search values are variants, if you pass multiple values, you must either pass a variant array type as an argument (for example, the return values from the Lookup method), or you must construct the variant array on the fly using the VarArrayOf function. The following code illustrates a search on multiple columns using multiple search values and partial-key matching:

with CustTable do

  Locate('Company;Contact;Phone', VarArrayOf(['Sight Diver','P']), loPartialKey);

Locate uses the fastest possible method to locate matching records. If the columns to search are indexed and the index is compatible with the search options you specify, Locate uses the index. Otherwise, Locate creates a BDE filter for the search.

Using Lookup

Lookup searches for the first row that matches specified search criteria. If it finds a matching row, it forces the recalculation of any calculated fields and lookup fields associated with the dataset, then returns one or more fields from the matching row. Lookup does not move the cursor to the matching row; it only returns values from it.

In its simplest form, you pass Lookup the name of column to search, the field value to match, and the field or fields to return. For example, the following code looks for the first row in the CustTable where the value in the Company column is "Professional Divers, Ltd.", and returns the company name, a contact person, and a phone number for the company:

var

  LookupResults: Variant;

begin

with CustTable do

  LookupResults := Lookup('Company', 'Professional Divers, Ltd.', 'Company;
    Contact; Phone');

end;

Lookup returns values for the specified fields from the first matching record it finds. Values are returned as variants. If more than one return value is requested, Lookup returns a variant array. If there are no matching records, Lookup returns a Null variant. For more information about variant arrays, see the Object Pascal Language Guide.

The real power of Lookup comes into play when you want to search on multiple columns and specify multiple values to search for. To specify strings containing multiple columns or result fields, separate individual in the string items with semi-colons.

Because search values are variants, if you pass multiple values, you must either pass a variant array type as an argument (for example, the return values from the Lookup method), or you must construct the variant array on the fly using the VarArrayOf function. The following code illustrates a lookup search on multiple columns:

var

  LookupResults: Variant;

begin

with CustTable do

  LookupResults := Lookup('Company; City', VarArrayOf(['Sight Diver', 'Christiansted']),
    'Company; Addr1; Addr2; State; Zip');

end;

Lookup uses the fastest possible method to locate matching records. If the columns to search are indexed Lookup uses the index. Otherwise, Lookup creates a BDE filter for the search.


Handling transactions

There are two ways to manage transactions in a Delphi database application using implicit and explicit control. It especially focuses on:

Using the methods and properties of the TDatabase component that enable explicit transaction control.

Using passthrough SQL with TQuery components to control transactions.

Delphi also supports local transactions, transactions made against local Paradox and dBASE tables.

Understanding transactions

When you create a database application using Delphi, Delphi provides transaction control for all database access, even against local Paradox and dBASE tables. A transaction is a group of actions that must all be carried out successfully on one or more tables in a database before they are committed (made permanent). If one of the actions in the group fails, then all the actions fail.

Transactions ensure database consistency even if there are hardware failures. It also maintains the integrity of data while permitting concurrent multiuser access.

For example, an application might update the ORDERS table to indicate that an order for a certain item was taken, and then update the INVENTORY table to reflect the reduction in inventory available. If there were a hardware failure after the first update but before the second, the database would be in an inconsistent state because the inventory would not reflect the order entered. Under transaction control, both statements would be committed at the same time. If one statement failed, then both would be undone (rolled back).

Using implicit transactions

By default, Delphi provides implicit transaction control for your applications through the Borland Database Engine (BDE). When an application is under implicit transaction control, Delphi uses a separate transaction for each record in a dataset that is written to the underlying database. It commits each individual write operation, such as Post and AppendRecord.

Using implicit transaction control is easy. It guarantees both a minimum of record update conflicts and a consistent view of the database. On the other hand, because each row of data written to a database takes place in its own transaction, implicit transaction control can lead to excessive network traffic and slower application performance.

If you explicitly control transactions, you can choose the most effective times to start, commit, and roll back your transactions. When you develop client applications in a multi-user environment, particularly when your applications run against a remote SQL server, such as Sybase, Oracle, Microsoft SQL, or InterBase,® or remote ODBC-compliant databases such as Access and FoxPro, you should control transactions explicitly.

Note:	If you used cached updating, you may be able to minimize the number of transactions you need to use in your applications. For more information about cached updates, see Chapter 15, "Working with cached updates," in the Database Application Developer's Guide.

Working with explicit transaction control

There are two mutually exclusive ways to control transactions explicitly in a Delphi database application:

Use the methods and properties of the TDatabase component.

Use passthrough SQL in a TQuery component. Passthrough SQL is only meaningful in the Delphi Client/Server Suite, where you use SQL Links to pass SQL statements directly to remote SQL or ODBC servers.

The main advantage to using the methods and properties of a database component to control transactions is that it provides a clean, portable application that is not dependent on a particular database or server.

The main advantage to passthrough SQL is that you can use the advanced transaction management capabilities of a particular database server, such as schema caching. To understand the advantages of your server’s transaction management model, see your database server documentation.

Using TDatabase methods and properties

The following table lists the methods and properties of TDatabase that are specific to transaction management, and describes how they are used:

Method or property	Purpose

Commit	Commits data changes and ends the transaction.

Rollback	Undoes data changes and ends the transaction.

StartTransaction	Starts a transaction.

TransIsolation	Specifies the transaction isolation level for a transaction.

StartTransaction, Commit, and Rollback are methods your application can call at run time to start transactions, control the duration of transactions, and save or discard changes made to the database.

TransIsolation is a database component property that enables you to control how a transaction interacts with other simultaneous transactions when they work with the same tables. In particular, it affects how much a transaction "sees" of other transactions’ changes to a table.

Starting a transaction

When you start a transaction, all subsequent statements that read from and write to the database occur in the context of that transaction. Each statement is considered part of a group. Write changes made by any statement in the group must be successfully committed to the database, or every change made by every write statement made in the group are undone.

Grouping statements is useful when statements are dependent upon one another. Consider a bank transaction at an Automated Teller Machine (ATM). When a customer decides to transfer money from a savings account to a checking account, two changes must take place in the bank’s database records:

The savings account must be debited.

The checking account must be credited.

If, for any reason, one of these actions cannot be completed, then neither action should take place. Because these actions are so closely related, they should take place within a single transaction.

To start a transaction in a Delphi application, call a database component’s StartTransaction method:

DatabaseInterBase.StartTransaction;

All subsequent database actions take place in the context of the newly started transaction until the transaction is explicitly terminated by a subsequent call to Commit or Rollback.

How long should you keep a transaction going? Ideally, only as long as necessary. The longer a transaction is active, the more simultaneous users that access the database, and the more concurrent, simultaneous transactions that start and end during the lifetime of your transaction, the greater the likelihood that your transaction will conflict with another when you attempt to commit your changes.

Committing a transaction

To make database changes permanent, a transaction must be committed using a database component’s Commit method. Executing a commit statement saves database changes and ends the transaction. For example, the following statement ends the transaction started in the previous code example:

DatabaseInterBase.Commit;

Note:	Commit should always be attempted in a try...except statement. If a transaction cannot commit successfully, you can attempt to handle the error, and perhaps retry the operation.

Rolling back a transaction

To discard database changes, a transaction must roll back its changes using Rollback. Rollback undoes a transaction’s changes and ends the transaction. For example, the following statement rolls back a transaction:

DatabaseInterBase.Rollback;

Rollback usually occurs in:

Exception handling code when you cannot recover from a database error.

Button or menu event code, such as when a user clicks a Cancel button.

Using the TransIsolation property

TransIsolation specifies the transaction isolation level for a database component’s transactions. Transaction isolation level determines how a transaction interacts with other simultaneous transactions when they work with the same tables. In particular, it affects how much a transaction "sees" of other transaction’s changes to a table. Before changing or setting TransIsolation, you should be familiar with transactions and transactions management in Delphi.

The default setting for TransIsolation is tiReadCommitted. The following table summarizes possible values for TransIsolation and describes what they mean:

Isolation level	Meaning

tiDirtyRead	Permit reading of uncommitted changes made to the database by other simultaneous transactions. Uncommitted changes are not permanent, and might be rolled back (undone) at any time. At this level your transaction is least isolated from the changes made by other transactions.

tiReadCommitted	Permit reading only of committed (permanent) changes made to the database by other simultaneous transactions. This is the default isolation level.

tiRepeatableRead	Permit a single, one time reading of the database. Your transaction cannot see any subsequent changes to data by other simultaneous transactions. This isolation level guarantees that once your transaction reads a record, its view of that record will not change. At this level your transaction is most isolated from changes made by other transactions.

Database servers may support these isolation levels differently or not at all. If the requested isolation level is not supported by the server, then Delphi will use the next highest isolation level. The actual isolation level used by each type of server is listed in the following table. For a detailed description of how each isolation level is implemented, see your server documentation.

TransIsolation setting	Paradox and dBASE	Oracle	Sybase and Microsoft SQL servers	InterBase
Dirty read	Dirty read	Read committed	Read committed	Read committed
Read committed (Default)	Not supported	Read committed	Read committed	Read committed
Repeatable read	Not supported	Repeatable read  (READ ONLY)	Not supported	Repeatable Read
Note:	When using transactions with local Paradox and dBASE tables, set TransIsolation to tiDirtyRead instead of using the default value of tiReadCommitted. A BDE error is returned if TransIsolation is set to anything but tiDirtyRead for local tables

If an application is using ODBC to interface with a server, the ODBC driver must also support the isolation level. For more information, see your ODBC driver documentation.

Using passthrough SQL

To be able to use passthrough SQL to control a transaction you must:

Use the Delphi Client/Server Suite.

Install the proper SQL Links drivers. If you chose the Standard installation when installing Delphi, all SQL Links drivers are already properly installed.

Configure your network protocol correctly. See your network administrator for more information.

Have access to a database on a remote server.

Use the BDE Configuration utility to set SQLPASSTHROUGHMODE to NOT SHARED.

With passthrough SQL, you use a TQuery, TStoredProc, or TUpdateSQL component to send an SQL transaction control statement directly to a remote database server. The BDE does not process the SQL statement. Using passthrough SQL enables you to take direct advantage of the transaction controls offered by your server, especially when those controls are non-standard.

Setting SQLPASSTHROUGHMODE

SQLPASSTHROUGHMODE specifies whether the BDE and passthrough SQL statements can share the same database connections. In most cases, SQLPASSTHROUGHMODE is set to SHARED AUTOCOMMIT. If however, you want to pass SQL transaction control statements to your server, you must use the BDE Configuration utility to set the BDE SQLPASSTHROUGHMODE to NOT SHARED. For more information about SQLPASSTHROUGH modes, see the online help for the BDE Configuration utility.

Note:	When SQLPASSTHROUGHMODE is NOT SHARED, you must use separate database components for TQuery components that pass SQL transaction statements to the server and those other dataset components that do not.

Using local transactions

The BDE supports local transactions against Paradox and dBASE tables. From a coding perspective, there is no difference to you between a local transaction and a transaction against a remote database server.

When a transaction is started against a local table, updates performed against the table are logged. Each log record contains the old record buffer for a record. When a transaction is active, records that are updated are locked until the transaction is committed or rolled back. On rollback, old record buffers are applied against updated records to restore them to their pre-update states.

Understanding the limitations of local transactions

The following limitations apply to local transactions

Automatic crash recovery is not provided.

Data definition statements are not supported.

For Paradox, local transactions can only be performed on tables with valid indexes. Data cannot be rolled back on Paradox tables that do not have indexes.

Transactions cannot be run against temporary tables.

Transactions cannot be run against the BDE ASCII driver.

Closing a cursor on a table during a transaction rolls back the transaction unless:

Several tables are open.

The cursor is closed on a table to which no changes were made.


Specfiying a data source

A TDataSource component is a nonvisual database component that acts as a conduit between a dataset and data-aware components on a form that enable the display, navigation, and editing of the data underlying the dataset. All datasets must be associated with a data source component in order for their data to be displayed and manipulated in data-aware controls on a form. Similarly, each data-aware control needs to be associated with a data source component in order to have data to display and manipulate. You also use data source components to link datasets in master-detail relationships.

You place a data source component in a data module or form just as you place other nonvisual database components. You should place at least one data source component for each dataset component in a data module or form.

Using TDataSource properties

TDataSource has only a few published properties. The following sections discuss these key properties and how to set them at design time and run time.

Setting the DataSet property

The DataSet property specifies the name of the dataset from which the a data source component gets its data. At design time you can select a dataset from the drop down list in the Object Inspector. At run time you can switch the dataset for a data source component as needed. For example, the following code swaps the dataset for the CustSource data source component between Customers and Orders:

with CustSource do

begin

  if DataSet = 'Customers' then

    DataSet := 'Orders'

  else

    DataSet := 'Customers';

end;

You can also set the DataSet property to a dataset on another form to synchronize the data controls on the two forms. For example:

procedure TForm2.FormCreate (Sender : TObject);

begin

  DataSource1.Dataset := Form1.Table1;

end;

Setting the Name property

Name enables you to specify a meaningful name for a data source component that distinguishes it from all other data sources in your application. The name you supply for a data source component is displayed below the component’s icon in a data module.

Generally, you should provide a name for a data source component that indicates the dataset with which it is associated. For example, suppose you have a dataset called Customers, and that you link a data source component to it by setting the data source component’s DataSet property to "Customers." To make the connection between the dataset and data source obvious in a data module, you should set the Name property for the data source component to something like "CustomersSource".

Setting the Enabled property

The Enabled property determines if a data source component is connected to its dataset. When Enabled is True, the data source is connected to a dataset.

You can temporarily disconnect a single data source from its dataset by setting Enabled to False. When Enabled is False, all data controls attached to the data source component go blank and become inactive until Enabled is set to True. It is recommended, however, to control access to a dataset through a dataset component’s DisableControls and EnableControls methods because they affect all attached data sources.

Setting the AutoEdit property

The AutoEdit property of TDataSource specifies whether datasets connected to the data source automatically enter Edit state when the user starts typing in data-aware controls linked to the dataset. If AutoEdit is True (the default), Delphi automatically puts the dataset in Edit state when a user types in a linked data-aware control. Otherwise, a dataset enters Edit state only when the application explicitly calls its Edit method.

Using TDataSource methods

TDataSource has three event handlers associated with it:

OnDataChange

OnStateChange

OnUpdateData

Using the OnDataChange event

OnDataChange is called whenever the cursor moves to a new record. When an application calls Next, Previous, Insert, or any method that leads to a change in the cursor position, then an OnDataChange is triggered.

This event is useful if an application is keeping components synchronized manually.

Using the OnUpdateData event

OnUpdateData is called whenever the data in the current record is about to be updated. For instance, an OnUpdateData event occurs after Post is called, but before the data is actually posted to the database.

This event is useful if an application uses a standard (non-data aware) control and needs to keep it synchronized with a dataset.

Using the OnStateChange event

OnStateChange is called whenever the state for a data source’s dataset changes. A dataset’s State property records its current state. OnStateChange is useful for performing actions as a TDataSource’s state changes.

For example, during the course of a normal database session, a dataset’s state changes frequently. To track these changes, you could write an OnStateChange event handler that displays the current dataset state in a label on a form. The following code illustrates one way you might code such a routine. At run time, this code displays the current setting of the dataset’s State property and updates it every time it changes:

procedure TForm1.DataSource1.StateChange(Sender:TObject);

var

  S:String;

begin

  case CustTable.State of

    dsInactive: S := 'Inactive';

    dsBrowse: S := 'Browse';

    dsEdit: S := 'Edit';

    dsInsert: S := 'Insert';

    dsSetKey: S := 'SetKey';

  end;

  CustTableStateLabel.Caption := S;

end;

Similarly, OnStateChange can be used to enable or disable buttons or menu items based on the current state:

procedure Form1.DataSource1.StateChange(Sender: TObject);

begin

  CustTableEditBtn.Enabled := (CustTable.State = dsBrowse);

  CustTableCancelBtn.Enabled := CustTable.State in [dsInsert, dsEdit, dsSetKey];

  ...

end;


Database Editor dialog box

The Database Editor dialog box sets up the properties of a database that specify the connection that should be made to a database. This dialog box allows you to specify the type of database, the connection parameters, what should happen when the user activates a connection, and whether the database is persistent.

These properties of the database component, as well as others, can also be specified using the Object inspector.

To display the Database Editor dialog box, double click on a database component.

Dialog box options

Name

Specifies the name of the database.  This name is used to refer to the database component from within the code of your application.

Alias

Specifies the BDE alias for the database. Choose a database aliases from the drop-down list.  This list contains all aliases currently registered with the BDE.  If you do not want to connect to a database that is registered as a BDE alias, you can set the Driver property instead.  If you set the Alias property, the Driver property is cleared, as the driver type is implicit in the BDE alias.

Driver

Specifies the type of database represented by the database component.  Choose a driver type such as STANDARD, ORACLE, SYBASE, or INTERBASE from the drop-down list. If the database server has an alias registered with the BDE, you can set the Alias instead.  Setting the Driver automatically clears the Alias property, to avoid potential conflicts with the driver type implicit in the database alias.

Parameter overrides

Specifies the values of all login parameters when connecting to the database. The specific parameters depend on the type of database.  To obtain a list of all parameters, as well as their default values, click the Defaults button.  You can then modify the default values to the values you want to use.

Defaults

Press the Defaults button to set the Parameter overrides to the default values for the driver type.

Clear

Press the Clear button to remove all parameter overrides.

Login Prompt

Check the Login Prompt control to cause a login dialog to appear automatically when the user connects to the database.  Uncheck the Login Prompt control to prevent the automatic login dialog.  Most database servers (except for the file-based STANDARD types) require the user to supply a password when connecting to the database.  For such servers, if the automatic login prompt is omitted, the application must supply the user name and password in some other manner.  These can be supplied either by providing hard-coded parameter overrides, or by supplying an OnLogin event handler that sets the values for these parameters.

Keep inactive connection

Check Keep inactive connection to indicate that the application should remain connected to the database even if no datasets are currently open. For connections to remote database servers, or for applications that frequently open and close datasets, checking Keep inactive connection reduces network traffic, speeds up applications, and avoids logging in to the server each time the connection is reestablished. Uncheck Keep inactive connection to cause the database connection to be dropped when there are no open datasets. Dropping a connection releases system resources allocated to the connection, but if a dataset is later opened that uses the database, the connection must be reestablished and initialized.


Creating Data Dictionary attribute sets for field components

When several fields in the datasets used by your application share common formatting properties (such as Alignment, DisplayWidth, DisplayFormat, EditFormat, MaxValue, MinValue, and so on), it is more convenient to set the properties for a single field, then store those properties as an attribute set in the Data Dictionary. Attribute sets stored in the data dictionary can be easily applied to other fields.

To create an attribute set based on a field component in a dataset:

1.	Double-click the dataset to invoke the Fields editor.

2.	Select the field for which to set properties.

3.	Set the desired properties for the field in the Object Inspector.

4.	Right-click the Fields editor list box to invoke the SpeedMenu.

5.	Choose Save Attributes to save the current field’s property settings as an attribute set in the Data Dictionary.

The name for the attribute set defaults to the name of the current field. You can specify a different name for the attribute set by choosing Save attributes as instead of Save attributes from the SpeedMenu.

Note:	You can also create attribute sets directly from the Database Explorer. When you create an attribute set from the data dictionary, the set is not applied to any fields, but you can specify two additional attributes in the set: a field type (such as TFloatField, TStringField, and so on) and a data-aware control (such as TDBEdit, TDBCheckBox, and so on) that is automatically placed on a form when a field based on the attribute set is dragged onto the form.


Associating Data Dictionary attribute sets with field components

When several fields in the datasets used by your application share common formatting properties (such as Alignment, DisplayWidth, DisplayFormat, EditFormat, MaxValue, MinValue, and so on), and you have saved those property settings as attribute sets in the Data Dictionary, you can easily apply the attribute sets to fields without having to recreate the settings manually for each field. In addition, if you later change the attribute settings in the Data Dictionary, those changes are automatically applied to every field associated with the set the next time field components are added to the dataset.

To apply an attribute set to a field component:

1.	Double-click the dataset to invoke the Fields editor.

2.	Select the field for which to apply an attribute set.

3.	Right-click the Fields editor list box to invoke the SpeedMenu.

4.	Choose Associate attributes.

5.	Select or enter the attribute set to apply from the Attribute set name dialog box. If there is an attribute set in the Data Dictionary that has the same name as the current field, that set name appears in the edit box.


Unassociating a Data Dictionary attribute set from a field component

If you change your mind about associating an attribute set with a field component, you can easily remove the attribute set from the field component:

1.	Double-click the dataset to invoke the Fields editor.

2.	Select the field for which to remove an attribute set.

3.	Right-click the Fields editor list box to invoke the SpeedMenu.

4.	Choose Unassociate attributes.

After you remove an attribute set from a field component, you can either use the Object Inspector to set its properties, or you can associate a different attribute set with the component.


What's new in Delphi 30
What’s new in Delphi 3

Delphi 3 offers the following new features and enhancements:

Package support

Code insight

Component templates

New documentation design

Visual Component Library enhancements

Run Time Library enhancements

Compiler changes

Multibyte character support

New Borland Database Engine (BDE) and SQL Links

Distributed datasets (Client/Server only)

COM support and ActiveX creation (Professional and Client/Server only)

Internet and Intranet development (Client/Server only)

OLEnterprise with Business ObjectBroker (Client/Server only)

New demo applications

Miscellaneous IDE enhancements


Package support

Packages are special dynamic-link libraries used by Delphi applications, the Delphi IDE, or both. They allow code-sharing among applications, reducing executable size and conserving system resources. The Component Library has been re-engineered to take advantage of package technology. A specific package can be compiled without rebuilding the entire Library, and packages can be loaded on a per-project basis (so that only the packages used by the current project need to be loaded). All Delphi components are now installed in the IDE as packages. Packages can provide a solution for development of efficient application executables that need to be quickly and easily distributed.

The IDE has been enhanced in several ways to accommodate packages. A Package Editor and associated dialogs, including a package-creation expert, have been added. The Palette options page (accessed by selecting Component | Configure Palette, or by selecting Tools | Options and clicking the Palette tab) now displays the package where each component resides.

Using Packages


Code Insight

Delphi's Code Insight enhancements display context-sensitive pop-up windows within the Code editor. Code Insight provides templates, code completion, method parameter lists, and tool-tip expression evaluation. These features are updated dynamically as you work in the IDE. To configure Code Insight, choose Tools | Environment Options and select the Code Insight tab.

Code templates

Code templates are common programming statement that you can quickly  insert into your code. Templates are available for common Pascal syntactic structures such as "if ... then ..." and "while" statements and "try ... except" blocks.

To access code templates in the editor, press <control>-j.

To add or edit templates, select the Code Insight tab in the  Environment Options dialog. Code templates are not multibyte-enabled.

Code completion

Code completiong is a quick way to select methods, properties, events, or arguments to enter into your code”

Type an object or class name followed by a period (.), to display a pop-up list of valid method, property, and event names.

Type an assignment statement and press <control>-<spacebar>, to display a pop-up a list of valid arguments for the variable.

Code parameters

When you type a method name followed by a left parenthesis, a pop-up window displays a list of required arguments with their syntax.

Tool-tip expression evaluation

While an application is stopped in debug mode, place the mouse cursor over any variable or property name in the Code editor. The value of the variable or property is shown in a pop-up window.  (This feature works best if compiler Optimization is turned off in the Project Options dialog.)

More code editor enhancements

Visual enhancements for debugger support have been added. The Code editor window now displays a gutter and gutter glyphs, which can be configured or turned off in the Environment | Options dialog (select Tools | Environment Options | Display).

IME (input method editor) support has been added for Asian-language environments. Comments and string values can now contain multibyte characters. Object Pascal identifiers (names of variables, constants, etc.) cannot use multibyte characters.

Text searches are supported across entire projects, across specified directories, and across all open files. To specify files for a search, choose Search | Find In Files, or choose Search | Find and click the Find In Files tab.


Component Templates

Component templates are groups of components that you add to a form in a single operation. Templates allow you to configure components on one form, then save their arrangement and default properties on the Component palette for reuse on other forms.

Creating component templates


New documentation design

The documentation set has been redesigned to better address your needs.

User’s Guide

Provides all the information you will need to use key features of the Delphi user interface and the basic fundamentals of creating applications using Object Pascal. All this information is duplicated in Help.

Developer’s Guide

This is the consolidation of two former books on database application development and component writing, plus additional topics in one volume.

Provides in-depth information on all the intermediate-to-advanced programming topics you will need to create database applications, custom components, and Internet and intranet applications. It describes how to work with COM and ActiveX. All this information is duplicated in Help.

Object Pascal Language Guide

Describes the syntax and semantics of the Object Pascal language, including the formal definition of the language. All this information is duplicated in Help.

Visual Component Library Reference

You will find the Visual Component Library Reference much easier to use. It provides information on visual components, those most commonly used. The information is organized by object with properties, methods, and events subordinated to their respective objects. This method allows you to easily understand in which object a property, method, or event originated.

Help provides information on all the available Delphi objects and components. Press F1 to display Help on any highlighted object and then navigate through the object and component hierarchy.


VCL enhancements

The following components or component groups, objects, properties, methods, or events have been enhanced or are new to Delphi 3:

User interface design

Button and Tool Bar components (new)

Graphics enhancements

Input Method Editor (IME) Support (new)

TDatetimePicker (new)

TAnimate (new)

TPageControl, TTabControl

TSplitter (new)

TOpenPictureDialog and TSavePictureDialog (new)

IConFont, UpdateMEtricSettings, and CMSysFontChanged

Other components

Decision support components (new)

Chart components

QuickReport components

TThreadList (new)

COM/ActiveX

TOleContainer

TStaticText (new)

Database

DataSets

TClientDataSet (new)

DBRichEdit

DBCtrlGrid

TField

See Also

Internet and Intranet Development


TPageControl, TTabControl

TPageControl and TTabControl have new properties: HotTrack, ScrollOpposite, and TabPosition.

HotTrack, if set to True, highlights the caption of each tab as the mouse pointer passes over it. ScrollOpposite moves previous rows of tabs to the other side of the control when a tab in a later row is selected. TabPosition determines whether the tabs are arranged at the top or the bottom of the control.

See Also

About PageControl

TPageControl

TTabControl


TSplitter

A new visual component, TSplitter, appears on the Additional page of the Component palette. Splitters allow you to divide a form into arbitrary rectangular regions that can be resized by the user at run time.

See Also

About Splitter

Splitter


Chart Components

A new group of visual components -- TChart, TDBChart, and TQRChart -- let you create custom graphs on a Delphi form.

See TChart


QuickReport Components

The new QuickReport component set includes an Expert to create reports easily. Reports designed with QuickReport 1.0 are automatically converted at design time to version 2.0.


TDateTimePicker

A new visual component, TDateTimePicker, displays a list box for entering dates or times. Deployment of TDateTimePicker requires the latest version of COMCTL32.DLL (version 4.70, dated 8/9/96, approximately 370Kb). This file is available from Microsoft; if you've installed Internet Explorer 3.0 or MS Office 97, you already have it.

See DateTimePicker


TAnimate

A new visual component, TAnimate, plays Audio Visual Interleaved (AVI) clips. TAnimate works with uncompressed AVI files or AVI clips compressed using run-length encoding (RLE).

TAnimate can obtain its AVI clip from an AVI resource, an AVI file, or, if the application is running under Windows 95 or NT 4.0, from Shell32.dll. To see how TAnimate is used, look at the Webbrows demo application in Coolstuf directory on the Delphi CD.

See Also

About Animate

Animate


TStaticText

A non-editable text component like Label, except that it includes a window handle, which is useful when the component’s accelerator key must belong to a windowed control.  Use StaticText in ActiveX property pages to provide users with feedback on the current state of the application.

See StaticText


TOpenPictureDialog and TSavePictureDialog

Two new Common Dialog components, TOpenPictureDialog and TSavePictureDialog, appear on the Dialogs palette page. These components work like TOpenDialog and TSaveDialog, except that they include a "preview" region that displays the selected graphic file before opening or saving it.

See Also

OpenPictureDialog

Selecting files to open

SavePictureDialog

Saving files


Decision support components

In Delphi Client/Server, a new group of components facilitates data analysis for decision support. These components include TDecisionCube, a multidimensional array that caches cross-tabulated data from a dataset; TDecisionQuery, a special dataset object that simplifies use of Decision Cubes; TDecisionSource, which represents a pivot state on data from a Decision Cube; TDecisionGrid, which displays cross-tabulated data from a Decision Source; and TDecisionGraph, which displays Decision-Source data in dynamic graphs.

See Also

DecisionCube

Using decision cubes

DecisionQuery

Using decision queries

DecisionSource

Using decision sources

DecisionPivot

Using decision pivots

DecisionGrid

Creating decision grids

Using decision grids

DecisionGraph

Using decision graphs


Input Method Editor (IME) support

Visual components that support entry or editing of text now have two published properties that allow you to create IME-enabled controls for Asian-language versions of Windows:

ImeName, which points to an input method editor, lets you to select an IME specifically for the component.

ImeMode controls the behavior of the IME.


Graphics enhancements

The following changes and enhancements have been made to Delphi's graphics support:

The InitGraphics routine is no longer required and has been deleted from graphics.pas.

Thread-safe support has been added for multithreaded operations to graphics classes.

TCanvas has two new methods: Lock and Unlock. The Lock method grabs exclusive access to a drawing canvas, and Unlock releases it. Background threads should always lock a canvas before drawing to it. The VCL will be updated to lock the canvas implicitly during normal VCL painting operations (Paint method and OnPaint events).

Bitmap objects have implicit thread locks only in the Assign operation. If you need to have multiple threads writing to the same bitmap object, you should lock the bitmap's canvas to synchronize access to the bitmap.

New properties have been added to TBitMap:

property DIBMemory: Pointer;

This returns a pointer to the image pixel data in memory for direct modification of the image. This property is read-only.

TBitmapHandleType = (bmDIB, bmDDB);

property HandleType: TBitmapHandleType;

Changing HandleType converts an internal bitmap handle from DIB to device format, or vice-versa. Some color formatting may be lost in the conversion.

TBitmap has been enhanced to use DIB sections instead of memory streams. This reduces system memory use by 50% and preserves the pixel format of the original file even through bitmap modifications.

Support has been added for 15bpp, 16bpp, and 32bpp BMP pixel formats, and for optional color palettes in high-color BMP files. Palettes assigned to high-color bitmaps will be stored in the BMP file. Dithering and halftone palettes are supported when the destination bpp is less than the source bpp.

Palette realization has been removed from bitmap loading code. This eliminates screen flicker when loading forms with 256-color bitmaps, and improves bitmap and form load times by a factor of ten or more in large applications.

TGraphic has two new properties. Graphic objects besides TBitmap can now indicate palette and transparency:

property Transparent: Boolean;

property Palette: HPALETTE.

When Transparent is True, the image does not completely cover its rectangular area.

The read-only property Palette is a handle to the color palette of the image. If the graphic doesn't use palettes,Palette is 0.

TImage no longer assumes TBitmap's palette and transparency settings. Instead it uses TGraphic's generic Palette and Transparent properties. TImage.PictureChanged now initiates a palette realization, if necessary.


TThreadList

A new class, TThreadList, creates a thread-safe list. You can add or remove objects without explicit locks.


Button and Tool Bar components

Several new components and properties offer increased options for configuring buttons and tool bars. TToolBar and TCoolBar (on the Win32 palette page) offer improved control over the alignment of buttons at runtime. The Flat property of TToolBar, if set to True, lets graphics show through from under the tool bar. TSpeedButton also has a property called Flat that makes a button's border invisible until the user points to it with the mouse.

To see how TToolBar and TCoolBar work, look at the Webbrows demo application in Demos\Coolstuf on the Delphi CD. (This application, which also illustrates the TAnimate component, is similar to the Webbrows demo in Demos\Internet\Html.)

Note:	Deployment of TCoolBar, or of TToolBar with the Flat property set to True, requires the latest version of COMCTL32.DLL (version 4.70, dated 8/9/96, approximately 370Kb). This file is available from Microsoft; if you've installed Internet Explorer 3.0 or MS Office 97, you already have it.

See Also

Working with toolbars and cool bars.

ToolBar

About ToolBar

CoolBar

About CoolBar


TOleContainer

A new published property, AllowActiveDoc, has been added to TOleContainer. Set AllowActiveDoc to True to allow insertion of ActiveDocs (DocObjects) into the OleContainer; the OleContainer can respond when an embedded object requests an ActiveDoc interface.


IconFont, UpdateMetricSettings, and CMSysFontChanged

The IconFont property has been added to TScreen, and the UpdateMetricSettings property has been added to TApplication. These properties allow applications to use the Windows 95 Display Properties settings.

IconFont stores the icon font setting from Windows 95, and, if UpdateMetricSettings is True (the default), notifies top-level forms when the setting changes.

TControl has a new DesktopFont property.  When DesktopFont is True, TControl implements the CMSysFontChanged event to reset the Font property.


DataSets

A new public property has been added to TDataSet:

property CacheBlobs: Boolean; default True;

Setting this to True turns on blob caching, which improves performance when scrolling through records with on-screen blob fields (for example, in a DBCtrlGrid). Additional memory is used to store the cache.

In preparation for extending the functionality of datasets, BDE-specific dependencies have been removed from TDataSet and put into a new object, TDBDataSet. Some additional functionality is now found in TDBDataSet instead of TDataSet.TDataSet itself contains three new functions:

function IsEmpty: Boolean;

Checks for a dataset without records

function CompareBookmarks(Bookmark1, Bookmark2: TBookmark):Integer;

Compares two bookmarks and returns 0 if they are equal}

function BookmarkValid(Bookmark: TBookmark): Boolean;

Verifies that a bookmark is valid

All dataset code in DB.PAS that depends on the BDE has been moved from DB.PAS into DBTABLES.PAS. Existing code may have to be edited to convert references to variables of TDataSet to TDBDataSet or TBDEDataSet. Some methods and properties  have been moved from TDataSet to TDBDataSet or TBDEDataSet.

Note:	For dataset provider and InterBase tables, resolving a deleted or modified record requires a unique maintained index.


TField

A new function, IsBlob, has been added to TField:

function IsBlob: Boolean;

IsBlob returns True if the field is a blob field.


TClientDataSet

New components, including TClientDataSet, are used for multi-tiered database applications.

A demo application, in Demos\Midas\Empedit, uses TClientDataSet to create an editable employee table. The client application is in EMPEDIT.DPR and the server application is in SERVER.DPR.

Another demo application, in Demos\Midas\Mstrdtl, uses TClientDataSet to create a project manager. The client application is in CLNTPROJ.DPR and the server application is in SERVER.DPR; you must compile and run the server before running the client.

See Also

TClientDataSet

TMIDASConnection

TRemoteServer

Distributed Datasets

Creating and using a client dataset


DBRichEdit

The TDBRichEdit component (in the DBCtrls unit) permits the user to display and enter Rich Text Format (RTF) data in a memo or BLOB field.

TDBRichEdit = class(TCustomRichEdit)

Link the TDBRichEdit instance with a dataset by specifying a datasource component (TDataSource) that identifies the dataset as the value of the memo's DataSource property. Specify the field in the dataset  to access as the value of the DataField property. See the TDBMemo documention for further information on connecting the TDBRichEdit to a data source.

Applications must implement any user interface components necessary to make formatting operations available to the user. Refer to the RichEdit demo included in the Demos\RichEdit directory.

See Also

DBRichEdit

About DBRichEdit


DBCtrlGrid

DBCtrlGrid can now contain DBImage or DBMemo controls  as a result of new support for BLOB caching.

A new property has been added to TDBCtrlGrid:

property SelectedColor: TColor default clWindow;

This property sets the color of the selected panel in the TDBCtrlGrid. By default, SelectedColor is the same as the color of the DBCtrlGrid itself.

See Also

DBCtrlGrid

About DBControlGrid.


Distributed datasets

Delphi provides a new dataset component, TClientDataSet, that enables you to create database-independent dataset objects in your applications. Client datasets can be used in standalone, single-tiered database applications, or can be coupled with the new remote server component, TMIDASConnection, to create the client portion of multi-tiered database applications.

See Also

TClientDataSet

Creating and using a client dataset

TMIDASConnection

Establishing and maintaining a connection to an application server


COM support and ActiveX creation

Delphi provides wizards to quickly create industry-standard ActiveX controls from a VCL (Visual Component Library) control. ActiveX controls can be used with existing development tools, such as C++, Java, Visual Basic, or PowerBuilder. The ActiveForm wizard enables you to quickly convert a Delphi form into an ActiveX control container for other VCL controls. Wizards also allow you to deploy these ActiveX controls and ActiveForms to your Web site. The built-in Component Object Model (COM)support enables developers to create reusable, language interoperable business objects.

See Also

ActiveX classes and demo applications

Internet and intranet development

OLE and ActiveX overview

Creating ActiveX controls

Creating ActiveForms

ActiveX Web Deployment

ActiveX demos

TOleContainer

TStaticText


ActiveX classes and demo applications

Delphi includes ActiveX demos, an ActiveX Class Framework, and new tools to facilitate building ActiveX controls. To find the new tools, choose File|New and click the ActiveX tab.

Two ActiveX demos are included:

DelCtrls (in Demos\ActiveX\Delctrls) includes many standard Delphi components in an ActiveX control.

TRegSvr (in Demos\ActiveX\Tregsvr) is a utility that registers ActiveX servers.


Internet and Intranet development

If you have the Client/Server edition, Delphi provides you with several new components that make building Internet and Intranet applications easy. These components handle all communication with the Web server, so you can concentrate on the content of your Web server application without having to worry about the HTTP communications protocols. Specialized table producers create HTML to represent your datasets, while page producers organize other HTML content. If you want to build a specialized server or client application, socket components handle the low-level communications protocols, allowing you to concentrate on the service your application is providing.

See Also

Creating Internet server applications

Working with sockets

Internet components

Internet component demos


Internet component demos

New components have been added to enable the creation of Internet server DLLs.

The Chat demo (in Delphi 3\Demos\Internet\Chat) uses some of these components in a "Chat" application.

The WebServ demo (in Demos\Webserv) uses the new Internet components to create an ISAPI Web-server application; the first Web page shows a list of customers and company names, with hypertext links to each company's Web page.

Note:	A single project should not include both the Forms unit and the HTTPApp unit. Each of these units declares its own Application variable (TApplication in Forms and TWebApplication in HTTPApp), which Delphi uses for initialization on program startup. Including both units in a project will cause a runtime error.

Note:	Under Windows NT, you must have the correct user rights to debug a  DLL that is launched from a Web Server.. In the User Manager

1.	Choose Policies|User Rights.

2.	Select "Show advanced user rights".

3.	Choose "Act as part of the operating system" in the "Right" list.

4.	Add your name to the "Grant To" list.

See Also

Creating Internet server applications

Working with sockets

Internet components

Internet and intranet development


RTL enhancements
Run Time Library enhancements

Math Unit

New integer functions have been added to the Math unit:

function SumInt(const Data: array of Integer): Integer register;

function MinIntValue(const Data: array of Integer): Integer;

function MaxIntValue(const Data: array of Integer): Integer;

These functions work like the corresponding floating-point functions Sum, MinValue, and MaxValue.

System Unit

See Compiler Changes.

SysUtils Unit

The SysUtils unit contains new functions and procedures:

function CompareMem(P1, P2: Pointer; Length: Integer): Boolean;
assembler.

CompareMem compares Length bytes of memory referenced by P1 to Length bytes of memory referenced by P2, and returns True if the memory segments are the same.

procedure RaiseLastWin32Error;

procedure Win32Check(RetVal: BOOL);

RaiseLastWin32Error retrieves the code for the last occurring Win32 error and raises an exception with the error code and associated message. Win32Check is used to verify the value of any Win32 API function that returns a BOOL to indicate success; if the function returns False (indicating failure), Win32Check calls RaiseLastWin32Error to raise an exception.


Multibyte character support

Several functions have been added or modified in the run time library to support multibyte characters. AnsiLowerCase, AnsiUpperCase, ChangeFileText, ExtractFileDir, ExtractFileExt, ExtractFileName, and ExtractFilePath have been modified.

The following functions are new:

AnsiCompareFileName	AnsiExtractQuotedStr

AnsiLastChar	AnsiLowerCaseFileName

AnsiPos	AnsiQuotedStr

AnsiStrComp	AnsiStrIComp

AnsiStrLastChar	AnsiStrLComp

AnsiStrLIComp	AnsiStrLower

AnsiStrPos	AnsiStrRScan

AnsiStrScan	AnsiStrUpper

AnsiUpperCaseFileName	ByteToCharIndex

ByteToCharLen	ByteType

CharToByteIndex	CharToByteLen

IsDelimiter	IsPathDelimiter

LastDelimiter	StrByteType

Note:	The ByteType and StrByteType functions require an index parameter, but do not perform bounds checking. Hence, passing a very large number to one of these functions can cause an access violation.

Finally, the LeadBytes global variable has been added:

LeadBytes: set of Char = [];

TFont.Charset property
When using a multibyte character set, set the TFont.Charset property.

Charset is an integer that represents the character set selected for the text object where the TFont instance occurs. For Asian characters to display properly, Charset must point to a character set that contains those characters. The easiest way to set Charset is to open the Object Inspector's Font dialog for the text object (such as TLabel) and select a font from the list box.

The TTextAttributes object used by Rich Edit controls also supports the Charset property.


New Borland Database Engine (BDE) and SQL Links

Delphi includes a new versions of the BDE 4.0 and SQL Links. Most files associated with the BDE are in \Program Files\Borland\Delphi 3\BDE. For information about BDE and SQL Links 4.0, see Readlink.txt. Help for the BDE is in BDE32.HLP and BDE32.CNT.

To configure the BDE, you can launch the following driver configuration utilities from within the Database Explorer:

ODBC Administrator for ODBC drives: Click Databases at the top of the Databases tab. Then choose Object | ODBC Administrator.

Vendor-supplied configuration utilities for SQL drives: Click an alias on the Databases tab. Then choose Object | Vendor Configuration (or the name of the target utility, if it appears).

BDE Administrator for the BDE and native BDE/SQL Links drivers: Choose Object | BDE Administrator. Online Help for the the BDE Administrator (BDEADMIN.EXE) is in BDEADMIN.HLP and BDEADMIN.CNT.

New BDE features include:

FoxPro support

The dBASE driver now includes support for FoxPro compressed index (.CDX) and BLOB (.FPT) files, letting you open and create  FoxPro 2.0, 2.5, and 2.6 tables. To create a FoxPro table with the dBASE driver, set the LEVEL driver configuration parameter to 25 using the Configuration page of the BDE Administrator.

To create FoxPro tables using the BDE API, pass the optional parameter LEVEL with a value of 25 when using DbiCreateTable to create a dBASE table.

To add a FoxPro index, set curTABLELEVEL  to 25 using DbiSetProp before calling DbiAddIndex. DbiSetProp will return an error if the table is already using dBASE index or BLOB files.

To see if a cursor is referencing a FoxPro table, retrieve CURProps using DbiGetCursorProps and check if CURProps.iTblLevel is equal to FOXLEVEL25.

Microsoft Access support

If you have a version of the Microsoft JET engine (included with Microsoft Access and FoxPro) installed on your system, you can now use the BDE to open or create Microsoft Access tables using the MSACCESS driver.

Note:	BCD (binary coded decimal) support is not yet implemented for this driver. Setting the BCD Enabled parameter to True has no effect.

In the BDE API, use the constant szMSACCESS when creating Access tables or checking the table type. The following table lists new physical data types for MSACCESS and their BDE logical equivalents:

MSACCESS Physical Datatype	BDE Logical Equivalent

fdACCAUTOINC	fldINT32, fldstAUTOINC

fldACCBIT	fldBOOL

fldACCBYTE	fldUINT16

fldACCCHAR	fldZSTRING

fldACCDATETIME	fldTIMESTAMP

fldACCDOUBLE	fldFLOAT

fldACCFLOAT	fldFLOAT

fldACCLONG	fldINT32

fldACCLONGBINARY	fldBLOB, fldstACCOLEOBJ

fldACCLONGTEXT	fldBLOB, fldstMEMO

fldACCMONEY	fldFLOAT, fldstMONEY

fldACCSHORT	fldINT16

fldACCVARCHAR	fldZSTRING

ODBC 3.0 support

The new BDE supports ODBC 3.0 drivers.

Multibyte character support

The new BDE is fully multibyte-enabled.

BDE Provider support

The BDE Provider technology is used by Delphi's new distributed DataSets. For more, see Understanding BDE-enabled datasets

Data Dictionary enhancements

The Data Dictionary can now store domain and table constraints. These can be propagated to the client using remote DataSets or enforced using BDE cursors.

Parameter binding support for BLOBs and long strings (greater than 255 characters)

BDE now supports parameter binding for BLOBs and strings longer than 255 characters. See the description of DbiQSetParams in BDE32.HLP for details.

Oracle 7.3 stored procedures

Oracle 7.3 stored procedures can now return result sets.

SQL Monitor

The SQL Monitor now provides more information on SQL transactions.

Configurable BLOB caching

You can now use the Database Explorer to configure the BDE’s BLOB caching.

See Also

DataSets

TField

TClientDataSet

DBCtrlGrid

DBRichEdit

Distributed datasets


SQL Database Explorer Enhancements

The following features have been added or enhanced in the SQL Database Explorer:

BDE administration

The SQL Database Explorer includes a page tab for configuring the Borland Database Engine.

SQL editing

Editing of some SQL objects (such as tables, views and stored procedures for all servers, Oracle packages and package bodies, InterBase generators and exceptions) is now supported.

Object menu commands that affect SQL objects include New, Rename, Delete, Apply, Cancel, and Save As. Remember that edits are temporary until they are applied. Back up your data before editing SQL objects.

View | Change Text Option

As you edit an SQL object, you can see the queries that will be executed when you apply your changes. The queries are listed in a read-only Changes area on the Text page. To show the Changes area, select View | Change Text.

SQL editing in the IDE Code Editor

If you invoke the SQL Database Explorer from the Delphi IDE, you can use the Code editor and its rich set of editing features to edit SQL objects. To use the Code editor,

1.	Choose Database | Explore.

2.	Select and SQL object to edit.

3.	Choose Edit | Text (ctrl-T) from the Explorer menu.

While you edit an SQL object in the code editor, the object's text is read-only in the Database Explorer.

Query Options

A new Query Options dialog (select Options | Query) sets options for queries executed on the Enter SQL page. The Live Queries option has been moved from the Options menu to the Query Options dialog. New options control the execution of query scripts, allowing you to configure the Database Explorer for limited compatibility with tools from server vendors such as Sybase isql.exe and Microsoft SQL Server saf.exe. (For example, to configure the Database Explorer for use with Sybase isql.exe, you would set the line delimiter to "go".) By default, the line delimiter is "---" and no statement delimiter is defined; a typical statement delimiter would be ";".

Save As Dialog

A new Save As menu item invokes a Save As dialog where you can specify a name for the SQL object to save. By default an object's current name is proposed. If you keep the current name, any changes you have made are applied to the current object. If you specify a new name, a new object is created and any editing changes are applied to the new object. Edits to the original object remain intact, but are not applied to the original object until you choose to apply them.

Saving Text to Files

If an SQL object has a text representation on the Text page, the local menu contains a Save Text to File command that enables you to save the object's text to a text file.

You can also save a change script to a text file, which can be useful when you want to reuse the script on different systems with the same SQL object. To save a change script, click or tab into the change script and choose Save Text to File.

Unless the change script is selected, choosing Save Text to File saves the object, not the script.

Data Page Local Menu Commands

The following commands have been added to the local menu for the Data page: Order, Range, and Filter. These commands invoke dialogs that let you specify data ordering, set a range, or filter a data set. When you explore a different SQL object, Order, Range, and Filter are automatically reset.

Object-specific Local Menus

Local menus now have object- and platform-specific commands for some objects. For example, when you are editing an Oracle package heading, function, or procedure, a Compile command appears on the local menu.

Filter and Transaction Control

The Enter SQL local menu provides the following commands for controlling filtering and transactions: Filter, Begin Transaction (or Commit Transaction and Rollback Transaction). To specify an isolation level for a transaction, choose Options | Transaction Isolation.

Drag-and-Drop Stored Procedures

Stored procedures are now available as a drag-and-drop source. You can create a TStoredProc component by dragging a stored procedure from the Database Explorer onto a Delphi form, just as you would with databases, tables, views, and columns.

Support for DB2 and Informix

The Database Explorer now supports DB2 for common servers and Informix 5, 6, and 7. DB2 support is also planned for MVS and AS/400.

Word Wrap

A Word Wrap option has been added to the Options menu. Select Word Wrap if you want text to wrap on the Text page. This option is especially useful for DB2 and Informix servers, which don't save the original line breaks for objects such as views.


OLEnterprise with Business ObjectBroker

Delphi Client/Server includes OLEnterprise, which provides an OLE/DCOM interface to any RPC or COM server. OLEnterprise lets application logic and data access be centralized on an application server; the server becomes a one-to-many provider of distributed business objects and processes.The ObjectBroker distributed name service indirectly binds applications to remote objects based on object availability.

Note:	Purchase of Delphi Client/Server does not include redeployment rights for OLEnterprise or the Business ObjectBroker. If you wish to deploy applications that use OLEnterprise, you must purchase separate deployment licenses. Contact your local Borland sales office for details.

OLEnterprise is installed separately from Delphi. The files are on the CD in the \Olent directory, which includes additional documentation. For installation instructions,see INSTALL.TXT. For more information about OLEnterprise, see Borland's Web site at http://www.borland.com/midas/.


Compiler changes

Package Support

Language extensions and compiler directives have been added to support packages.

See Using Packages

Interfaces

Language extensions have been added to support development of COM and ActiveX objects.

See OLE and ActiveX overview

Assertions

Assert is a standard procedure, declared in the System unit, that tests whether a Boolean expression is successful:

Assert(expr: Boolean [; msg: string]);

If the Boolean test fails Assert raises an exception. (EAssertionFailed is declared in the Sysutils unit.)

Assertions are a debugging tool. Use them to test for preconditions that should not fail. Assert provides an opportunity to intercept a condition that should halt a program and that might otherwise cause abnormal termination. If no user code catches the EAssertionFailed, then the RTL will issue runtime error 227 and halt the program.

Since assertions are not usually used at run-time in shipping versions of a product, compiler directives are provided that disable the generation of code for assertions. They are:

$ASSERTIONS ON/OFF	(long form)

$C +/- (short form)

These are global switches and take effect for the whole source file in which they are contained, regardless of their position in the file.  It is not possible to enable and disable assertions for something smaller than a source file.

Boolean Data Types

For compatibility with Visual Basic, the True value for data types ByteBool, WordBool, and LongBool is now represented as -1; False is still represented as 0. Internally, the compiler gives these data types the same range as Integer. Every effort has been made to minimize the code revision required to accommodate this change. However, bounds checking is no longer implemented for these types. If typecasting has been used, code may no longer work properly. Short-circuit evaluation functions as before.

The Boolean data type remains unchanged (True = 1, False = 0).


New demo applications

The following new demos are Installed with Delphi in the Delphi 3\Demos\... directory:

CHAT (in ...\Internet\Chat) is an Internet Chat application.

DelCtrls (in ...\ActiveX\Delctrls) includes standard Delphi components in an ActiveX control.

TRegSvr (in ...\ActiveX\Tregsvr) is a utility that registers ActiveX servers.

Webbrows (in ...\Coolstuf) uses the new TToolBar, TCoolBar, and TAnimate components in a Web browser.

WebServ (in ...\Webserv) is a Web-server database application.

EmpEdit (in ...\Midas\EmpEdit) uses TClientDataSet to create an editable employee table.

Project Manager (in ...\Midas\Mstrdtl) uses TClientDataSet and TProvider in a master/detail application.

The projects in ...\Midas\SetParam illustrate the IProvider SetParams method.

The projects in ...\Midas\Adhoc perform ad hoc queries using the provider's OnDataRequest event.

...\Midas\USQLProv) shows a custom TProvider component that implements business rules.

...\Midas\ActiveFM contains an ActiveForm that uses client datasets.

Not all demos are included with every version of Delphi. Additional demos can be found on the Borland Web site.

For more information about demo applications, see Readme.txt.


Miscellaneous IDE enhancements

DLL Debugging

You can now specify an .EXE file to run for the purpose of debugging a DLL project. Choose Run | Parameters and enter the name of the .EXE file in the "Host application" edit box.

Unit Output Directory

The Directories/Conditionals page of the Project Options dialog has a new edit box called Unit Output Directory. Use this to specify the directory where compiled unit files are stored. Project .DLL and .EXE files are still sent to the regular Output Directory.

Shared Repository Directory

The Preferences page (under Tools | Options) now has an edit box for specifying the Shared Repository Directory. This location is stored in the Windows registry. In Delphi 2.0 it had to be be changed manually in the Windows Registry Editor (by creating a String Value called "BaseDir" in the "HKEY_CURRENT_USER | Software | Borland | Delphi | 2.0 | Repository" key).

Version Information

You can now include version information in your Delphi projects. Click the VersionInfo tab in the Project Options dialog (Project | Options) and select Include Version Information in Project. In addition to updating version information manually before recompiling a project, you can select the Auto-increment Build Number option. Version information appears on the Version page of the Windows Properties dialog for the project's .EXE file.

Compatibility Issues

Some of the changes introduced by Delphi 3 may require a few adjustments to your Delphi 2 applications.  See Compatibility with Version 2 for detailed information.


Compatibility With Version 2

Where possible, Delphi tries to provide backward compatibility with previous versions. Some of the new features in Delphi 3, however, require changes to the way applications are put together. Click here for a list of topics that are intended to ease the migration path of your application to Delphi 3.


Component Palette changes

See Also

Some components have been renamed, replaced, or moved to different palette pages.

The Win95 palette page is now called Win32, and contains additional controls.

The OCX page is now called ActiveX. The components on that page have changed as well.

VSSpell has been replaced by VCSpeller.

F1Book has been replaced by VCFormulaOne.

VtChart has been replaced by VCFirstImpression.

Graph has been replaced by GraphicsServer.

The FileListBox, DirectoryListBox, DriveComboBox, and FilterComboBox controls have moved from the System palette page to the Win 3.1 palette page.

Report has been removed from the Data Access page.  The Report component is still available for backward compatibility, but new applications should use the reporting components on the QReport page.

Check the Register procedure for your components, to see what Palette page they are installed on. If the Register procedure specifies a palette page that doesn’t exist, Delphi creates a new page. In some cases, this is what you want, but in other cases, the component is being registered on a page that has been renamed or replaced. For example, if your unit file contains the line

RegisterComponents(‘Win95’, [TNewComponent]);

replace it with

RegisterComponents(‘Win32’, [TNewComponent]);

to move your component to the renamed palette page.


Database changes

See Also

In order to prepare for extending the functionality of datasets, dependencies on the Borland Database Engine (BDE) have been removed from TDataSet and put into a new object, TBDEDataSet.  If your code refers to the TDataSet class, you may need to change it so that it references TBDEDataSet instead.  The following methods and properties have been moved from TDataSet to TDBDataSet or TBDEDataSet:

procedure GetIndexInfo;	procedure FetchAll;
procedure ApplyUpdates;	procedure CommitUpdates;
procedure CancelUpdates;	procedure RevertRecords;
function UpdateStatus: TupdateStatus;	property UpdatesPending: Boolean;
property Handle: HDBICur;	property Locale: TLocale;
property UpdateObject: TdataSetUpdateObject;	property UpdateRecordTypes: TUpdateRecordTypes;
property ExpIndex: Boolean;	property KeySize: Word;
property SetDetailFields;
All dataset code in the DB unit that depends on the BDE has been moved to the DBTables unit, and the field component classes have been moved from the DBTables unit back to the DB unit.  You may need to change the uses clause in your files to include the DBTables unit. The following is a list of objects that have been moved from the DB unit to DBTables:

EDBEngineError	TDBError

TBDECallback	TLocale

TDatabase	TParamList

TDataSetUpdateObject	TSession

TDBDataSet	TSessionList

In addition to the field components (TAutoIncField, TBCDField, and so on), TIndexDef and TIndexDefs have moved from the DBTables unit to the DB unit.

The BDECalcField property has been removed from TField.  Use the expanded FieldKind property instead.  For TFieldDef, the BDECalcField property has been generalized and renamed to InternalCalcField.  The Transliterate property has been moved from TMemoField to its immediate ancestor TBlobField.


Changes to other objects

See Also

In addition to the changes in the data access components, some of the other objects in the Component library have  changed.  In most cases, code that uses the older components is automatically converted.  However, you may wish to check for the following cases.

TTreeView

The OnExpanded, OnExpanding, OnCollapsed, and OnCollapsing events now occur in all parent nodes no matter how the nodes are expanded or collapsed.

TForm

The TForm class has been divided into a TCustomForm base class and a TForm derived class.  In most cases, this change will not affect your code.  However, the global GetParentForm function now returns a TCustomForm object instead of a TForm object.  In addition, the form’s GetChildren method now has an additional parameter that specifies the form that must be searched.

ActiveX

Delphi 3 introduces new ActiveX wrapper classes to allow you much more power and flexibility when creating ActiveX controls.  The old Delphi 2 classes are still provided for backward compatibility.  Include the  OleAuto unit when you are using the Delphi 2 ActiveX objects.

QuickReports

Most reports designed with QuickReport 1.0 are automatically converted at design time to version 2.0.  However, the conversion process has some limitations.  Some reports from Delphi 2.0 may not load properly.

Graphics unit

The InitGraphics routine is no longer required and has been deleted from graphics.pas.

Errors

EOutOfResources and EInvalidOperation exception classes have moved from controls.pas to classes.pas.

TPropInfo

The PropType field of the TPropInfo record has changed from PTypeInfo to PPTypeInfo.  Component writers that provide design time property editors may need to adjust their code for the additional level of indirection.


Compiler Changes

See Also

For compatibility with Visual Basic, the True value for data types ByteBool, WordBool, and LongBool is now represented as -1.  False is still represented as 0.  Internally, the compiler gives these data types the same range as Integer.  Every effort has been made to minimize the code revision required to accomodate this change.  However, bounds checking is no longer implemented for these types.  If your code uses typecasting, it may no longer work properly.  Short-circuit logic behaves as before.  The Boolean data type remains unchanged (True = 1, False = 0).

The new CharSet property on fonts is not recognized by earlier versions of the compiler.  If you save a form in Delphi 3 (even if it was created in Delphi 2), all fonts are given the CharSet attribute set. After saving the form in Delphi 3, trying to run or recompile the form in Delphi 2 will generate an error.  You can tell Delphi 2 to continue running the form in spite of the error, and it will strip out the new property.


Changes to the Help System

See Also

Delphi 3 calls up help for components differently than Delphi 2 did.  If you are writing custom components and providing your own help system, you must use A linking instead of multihelp B footnotes to provide context-sensitive help.  For more information, see Providing context-sensitive help for your component.


Changes introduced by packages

See Also

Packages are special dynamic-link libraries used by Delphi applications, the Delphi IDE, or both.  They allow code-sharing among applications, reducing executable size and conserving system resources.  The Component Library has been re-engineered to take advantage of package technology.

The introduction of Packages may require some changes to your source code.  In particular, the ExitProc should be used only when generating .EXE files.  Do not use ExitProc within a dynamically loaded package, such as design-time code.  All ExitProc calls are now made before any of the finalization sections are executed.  Use finalization sections to implement exit behavior.  In your existing code, move ExitProc variables to the finalization section.

The use of packages also changes the way you must install third party components (DCUs).  For more information, see Using older third party components.

For more information about compiling and using packages, see Using packages.


Using older third-party components
Using older third-party components (DCUs)

See Also

In Delphi 3, all components are installed in the IDE as packages, which are special DLLs. If you are using third-party components that were supplied for a previous release of Delphi, you can’t install them “as is” in the new IDE. To use your old third-party components in Delphi 3, you must either

Get new (packaged) versions from the component vendor.

Compile the components into a package yourself.

Replace the components with the new or enhanced objects supplied with Delphi 3. Delphi 3 introduces several new objects that extend the functionality of Delphi to take advantage of recent enhancements to Windows.

If you get new, packaged versions of your components, follow the vendor’s instructions for their installation and use.

To compile third-party components into packages on your own, you must have complete source (.PAS) files for the components. If the vendor has supplied a “main” .PAS file that uses another file shipped only as a .DCU, you will not be able to compile the package. If you have several third-party components that you want to install, you can compile them all into a single package.

Before compiling third-party components into packages, check the source code for necessary changes. See the following topics for information on what you should look for:

Database Changes

Changes Introduced by Packages

Component Palette Changes

Compiling and installing a package

To compile a component and install it on the Component palette:

1.	Choose File|New and double-click the Package icon.

2.	In the New Package dialog, enter a file name (you need not include the extension), and, optionally, a description. You can use any valid name for your package file. Click OK.

3.	In the Package editor, select the Contains tab and click Add. With the Add Unit tab selected, enter the name of the component’s main source file in the Unit File Name edit box. Click OK.

4.	If you are including multiple components in a single package, and the components have different source files, repeat step 3 above. However, if the additional units are listed in the Uses clause of the main unit you are packaging, do not add those units to the package, they will be added automatically during compilation.

5.	Click Compile to compile the package. The compiler may ask permission to add other packages to the Requires clause of the package you are building. Click OK.

6.	After successfully compiling the package, click Install to install the components on the Component palette.

7.	Before closing the Package editor, right-click and choose Save Package from the speed menu. This saves the package source (.DPK) file.


Delphi for C++ Programmers
Information about Delphi for C++ programmers

C++ programmers who are new to Delphi should read the white papers in D3_CPP.DOC and BC5_D3.DOC. These Microsoft Word documents are located on the Delphi CD in the Info\Borland directory. For additional information, see Borland's Web site at http://www.borland.com/delphi/cpp/.


Delphi for VB Programmers
Information about Delphi for Visual Basic programmers

Visual Basic programmers who are new to Delphi should read the white paper in VB2DELPH.DOC. This Microsoft Word document is located on the Delphi CD in the Info\Borland directory. For additional information, see Borland's Web site at http://www.borland.com/delphi/vb/.


Getting started

A tour of the Delphi development environment

A tour of the Delphi development environment

Placing components on a form

Changing component appearance and behavior

Editing code with the Delphi Code editor

Debugging applications

Exploring databases

Flexible tool for storing objects as templates

Project file-management tool

Handy pop-up menus

Toolbar buttons for frequently used commands

Getting help

First application--a brief tutorial

First application--a brief tutorial

Starting a new application

Setting a property value at design time

Adding objects to the form

Accessing a database

Adding some spice

Final touches

Hooking up an event handler


Developing Applications

Making effective use of the development environment

Making effective use of the development environment

Browsing and designing with the SQL Explorer

Using the Data Dictionary

Creating multi-tiered database applications

Designing a database interface with data-aware controls

Using the Database Desktop with Paradox and dBASE files

Using data modules and remote data modules

Creating and using component templates

Using the Object Repository

Overview of Delphi's database architecture

Managing database sessions

Managing database sessions

Working with a session component

Managing multiple sessions

Using a session component in data modules

Connecting to databases

Connecting to databases

Understanding persistent and temporary database components

Controlling connections

Handling transactions

Applying cached updates for multiple datasets

Understanding database and session component interactions

Using database components in data modules

Understanding datasets

Understanding datasets

What is TDataSet?

Opening and closing datasets

Determining and setting dataset states

Searching datasets

Displaying and editing a subset of data using filters

Modifying data

Using dataset events

Understanding BDE-enabled datasets

Understanding BDE-enabled datasets

Overview of BDE-enablement

Handling database and session connections

Working with a provider component

Working with cached updates

Caching BLOBs

Responding to a server yield

Creating and using a client dataset

Creating and using a client dataset

Working with a client dataset

Sorting and indexing

Working with another view of a dataset

Creating a new dataset

Working with data sources

Working with data sources

Establishing a database-to-data control link

Working with field components

Working with field components

Understanding field components

Creating persistent fields

Arranging the order of persistent fields

Defining new persistent fields

Setting persistent field properties and events

Working with field component methods at runtime

Displaying, converting, and accessing field values

Checking a field's current value

Setting a default value for a field

Working with constraints

Working with tables

Working with tables

Using table components

Setting up a table component

Controlling read/write access to a table

Searching for records

Sorting records

Working with a subset of data

Deleting all records in a table

Deleting a table

Creating a table

Importing data from another table

Synchronizing tables linked to the same database table

Creating master/detail forms

Working with queries

Working with queries

Using queries effectively

What databases can you access with a query component?

Using a query component: an overview

Specifying the SQL statement to execute

Setting parameters

Executing a query

Preparing a query

Unpreparing a query to release resources

Creating heterogenous queries

Improving query performance

Working with result sets

Working with stored procedures

Working with stored procedures

When should you use stored procedures?

Using a stored procedure

Creating a stored procedure component

Understanding stored procedure parameters

Viewing parameter information at design time

Setting parameter information at design time

Setting input parameter values at run time

Preparing and executing a stored procedure

Binding parameters

Accessing output and result parameters

Accessing the result dataset

Working with Oracle overloaded stored procedures

Working with cached updates

Working with cached updates

Deciding when to use cached updates

Understanding the cached updates process

Enabling and disabling cached updates

Fetching records

Applying cached updates

Canceling pending cached updates

Undeleting a cached record

Checking update status

Determining if you need to control the updating process

Using update objects to update a dataset

Updating a read-only result set

Creating an OnUpdateRecord event handler

Handling cached update errors

Using data controls

Using data controls

Using common data control features

Displaying and editing data in a data-aware control

Displaying data as labels

Displaying and editing fields in an edit box

Displaying and editing text in a memo control

Displaying and editing text in a rich edit memo control

Displaying and editing graphics fields in an image control

Displaying and editing data in list and combo boxes

Displaying and editing data in lookup list and combo boxes

Handling Boolean field values with check boxes

Restricting field values with radio controls

Navigating and manipulating records

Working with grids

Working with grids

Viewing and editing data with TDBGrid

Creating a grid that contains other data-aware controls

Using decision support components

Using decision support components

About crosstabs

Guidelines for using decision support components

Using datasets with decision support components

Using decision cubes

Using decision sources

Using decision pivots

Creating and using decision grids

Creating and using decision graphs

Decision support components at runtime

Decision support components and memory control

Copying data and table structures

Copying data and table structures

Creating a batch move component

Specifying a batch move mode

Mapping data types

Executing a batch move

Handling batch move errors

Creating a multi-tiered application

Creating a multi-tiered application

A structural overview of a multi-tiered application

Advantages of the multi-tiered database model

Overview of creating a multi-tiered application

Establishing and maintaining a connection to an application server

Accessing data with client datasets

Creating a data provider for the application server

Supporting the briefcase model

Modifying a two-tiered application to use the multi-tiered model

2 Creating custom components

Overview of component creation

Overview of component creation

The Visual Component Library

Components and classes

How do you create components?

What goes in a component?

Creating a new component

Testing uninstalled components

Object-oriented programming for component writers

Object-oriented programming for component writers

Creating new classes

Ancestors, descendants, and class hierarchies

Controlling access

Dispatching methods

Abstract class members

Classes and pointers

Creating properties

Creating properties

Why create properties?

Types of properties

Publishing inherited properties

Defining properties

Creating array properties

Writing property editors

Creating events

Creating events

What are events?

Implementing the standard events

Defining your own events

Creating methods

Creating methods

Avoiding dependencies

Naming methods

Protecting methods

Making methods virtual

Declaring methods

Using graphics in components

Using graphics in components

Overview of graphics

Using the canvas

Working with pictures

Offscreen bitmaps

Responding to changes

Handling messages

Handling messages

Understanding the message-handling system

Changing message handling

Creating new message handlers

Registering components

Registering components

Registering components

Adding palette bitmaps

Providing Help for your component

Storing and loading properties

Creating packages

Creating packages

Runtime and design-time packages

Modifying an existing component

Modifying an existing component

Creating and registering the component

Modifying the component class

Creating a graphic component

Creating a graphic component

Creating and registering the component

Publishing inherited properties

Adding graphic capabilities

Customizing a grid

Customizing a grid

Creating and registering the component

Publishing inherited properties

Changing initial values

Resizing the cells

Filling in the cells

Navigating months and years

Navigating days

Making a control data-aware

Making a control data-aware

Creating a data-browsing control

Creating a data-editing control

Making a dialog box a component

Making a dialog box a component

Defining the component interface

Creating and registering the component

Creating the component interface

Testing the component

Building a dialog box into a DLL

Building a dialog box into a DLL

Adding the interface routine

Modifying Project file

Opening the dialog box from an application

2 Creating Internet and intranet applications

Creating Internet server applications

Creating Internet server applications

Terminology and standards

An overview of HTTP server activity

Web server applications

The structure of a Web server application

The Web dispatcher

Action items

Accessing client request information

Creating HTTP response messages

Generating the content of response messages

Using database information in responses

Debugging server applications

Working with sockets

Working with sockets

Implementing services

Describing socket connections

Describing the socket

Using socket components

Reading and writing over socket connections

Responding to socket events

2 Working with COM and ActiveX

OLE and ActiveX Overview

OLE and ActiveX Overview

An Overview of COM

Using interfaces in Delphi

OLE and ActiveX objects

OLE Automation interfaces

Delphi ActiveX Framework

Creating ActiveX controls

Creating ActiveX controls

Overview of ActiveX control creation

Creating an ActiveX control

Creating ActiveForms

Creating ActiveForms

Creating an ActiveForm

ActiveX Web Deployment

ActiveX Web Deployment

Deploying ActiveForms and ActiveX controls

Setting Web Deployment Options

Creating property pages

Creating property pages

Creating a property page

Creating OLE Automation servers

Creating OLE Automation servers

Overview of Automation server creation

Creating an Automation server

Working with type libraries

Working with type libraries

The Type Library editor


Workgroups menu
Workgroups menu: Delphi version control interface

Use the Workgroups menu to set up version control and manage workflow on projects involving many programmers. Delphi version control is an interface to Intersolv PVCS Version Manager 5.1 or later.

Workgroups menu commands:

Browse PVCS Projects	Displays the PVCS Project window in which you can perform all version control tasks and access help.
Manage Archive Directories	When the PVCS window is open, displays the Archive Directories dialog box that lets you add, remove, or create archive directories.
Add Project to Version Control	Lets you save the current project to a version control directory.
Set Data Directories	Displays the Data Directories dialog box to set location of public and private root directories.
Why use version control?

After you have created more than one application or source code file with Delphi, version control helps you organize and keep track of the programs. You might want to archive old versions of an application before you start to work on the new version. If you work with a team of programmers, you need a way to coordinate which programmers have access to certain files, so that one programmer does not accidentally lose, change, or overwrite another programmer's code.

A version control system is useful when managing multiple or complex programming projects. Version control is used to archive files, control access to files by locking, and manage multiple versions of your projects.

PVCS Version Manager lets you store all the successive revisions of each file in an archive. Each project consists of a set of archives. Programmers checkout working files (“workfiles”)  from the archives, modify them, then check them back in, indicating revisions made, author, and date.  The locking features of checkin and checkout prevent overwrites and out-of-sync problems.  Also, you can track which changes were made at every stage to every file in a project: the who, what, when, and why of every modification.

Note:	Team development support is included in Delphi Client/Server. If you install the PVCS Developer Toolkit or Version Manager programs after installing Delphi, see Enabling PVCS support


Enabling PVCS support

Delphi's version control system support should be enabled when you run the setup program.

However, if you install the PVCS Developer Toolkit or Version Manager programs after installing Delphi, you must:

1. 	After installing the PVCS product, add the following two lines to the file DELPHI.INI in your Windows directory:

[Version Control]

VCSManager=C:\Program Files\Borland\Delphi\Bin\STDVCS32.DLL

Note that the path given above should specify the directory in which you installed Delphi. If necessary, replace C:\Program Files\Borland\Delphi\Bin (the default) with the path required by your system.

Note that the PVCS runtime DLL’s must be on your DOS path for Delphi to find them.

1. 	Run Delphi. The Workgroups menu group appears in the Delphi menu bar.

1. 	Choose Workgroups | Browse PVCS Projects to display PVCS Project window.

2. 	Click the Help button for topics on using PVCS Version Manager.


Workgroups | Manage Archive Directories

See also

With the PVCS window open and a project selected in the left pane, choose Workgroups|Manage Archive Directories to specify or create directories for the PVCS Version Control archives that make up a project.  An archive stores all the successive revisions of an individual workfile.  Developers checkout workfiles from the archives, modify them, then check them back in, indicating revisions made, author, and date.

To use the Manage Archive Directories dialog box:

1.	Specify an archive directory in the Directory and Drives lists.

2.	To designate the specified directory an archive location, click Add.

3.	To remove an archive location from the Archive directories list, click Remove.

4.	Choose OK to save the archive directory specification.


Workgroups | Add Project to Version Control

See also

Choose Workgroups|Add Project to Version Control to create a new PVCS project for the current Delphi Project.


Workgroups | Browse PVCS Project

See also

Choose WorkgroupsIBrowse PVCS Projects to display the PVCS Project window in which you can perform all version control tasks. You can check in or check out files, add, edit, or remove labels, lock or unlock revisions, run visual comparison, and archive reports. The Help icon opens the PVCS Help system for details on version control tasks.


Mworkgroups|Set Data Directories
Workgroups | Set Data Directories

See also

Choose Workgroups|Set Data Directories to set directories for workfiles.  When you check out a workfile from the archive, it is located in the directory you specify in this dialog box.


Custom Expert menu item

You have pressed F1 on a menu item for an Expert that was installed externally. For more information regarding this item, select it to open the Expert. Help is available for externally installed Experts if provided by the developer of the Expert.


Custom Version Control menu item

You have pressed F1 on a menu item for a version control system that was installed externally. For more information regarding this item, select it to gain access to the version control system. Help may be available for your version control system depending on the particular installation.


See also

Workgroups menu

Workgroups|Browse PVCS Project

Workgroups|Add Project To Version Control

In the PVCS window, click the Help icon.

Enabling PVCS support


See also

Workgroups menu

Workgroups|Browse PVCS Project

Workgroups|Manage Archive Directories

In the PVCS window, click the Help icon.

Enabling PVCS support


See also

Workgroups menu

Workgroups|Manage Archive Directories

Workgroups|Add Project To Version Control

In the PVCS window, click the Help icon.

Enabling PVCS support


A tour of the Delphi development environment

Section topics	See also

You can start Delphi the same way you start any Windows-based application:

Doubleclick on the icon that was created by the Install program.

Locate and double-click the DELPHI32.EXE file (the default installation puts this file in your \Program Files\Borland\Delphi 3.0\Bin directory).

Choose Run from the Microsoft Windows Taskbar Start menu, and specify the path to DELPHI32.EXE.

You'll see some of the major tools in the Delphi development environment:


Placing components on a form

Section topics	See also

To build a Delphi application, you place components on a form, set their properties, and code their event handlers.

Components are the visible parts of an application, such as scroll boxes and buttons, and those that aren't visible in the UI, such as components that get information from databases, perform calculations, and manage other interactions.

Many components are provided on the Component palette, grouped by function. Others are available in the Delphi Visual Component Library.


Changing component appearance and behavior

Section topics	See also

You can easily customize the way a component appears and behaves in your application by using the Object Inspector. When a component has focus on the form, its properties and events are displayed in the Object Inspector:


Navigating among events

Section topics	See also

Click the Events tab in the Object Inspector to navigate among events that each component can handle. In the following figure Button2 is still selected, and it’s type is displayed: TButton.

Or, double-click in the value column and Delphi generates the skeleton code for the object’s default event, ready for your input.

You can also double-click the object in the form to generate the code for the default event.


Editing code with the Delphi Code editor

Section topics	See also

As you visually design the user interface for your application, Delphi generates the underlying Pascal code. As you select and modify the properties of forms and components, the results of those changes appear automatically in the source code, and vice versa.

You can modify the source files directly with any text editor, including the built-in Delphi Code editor. The changes you make are immediately reflected in the visual environment.

While in the Code editor, to get help on any element of the Object Pascal language, put your cursor in the language element and press F1.


The Code editor is completely customizable

Section topics	See also

The Delphi Code editor is a full-featured text editor. You can customize it in many ways to suit your preferences.

Set your own colors for color syntax highlighting on the Colors page. Set font and other properties on the Display page.


Debugging applications

Section topics	See also

Delphi provides an integrated debugger that helps you find and fix errors in your applications. The debugger lets you control program execution, monitor variable values and items in data structures, and modify data values while debugging.

You can use the features of the debugger to examine the current state of the program. You can debug a specific area of the program, execute it one line or instruction at a time, set breakpoints, or pause execution. By viewing the values of variables, the functions of the call stack, and the program output, you can check that the area of code you are examining is performing as designed.


Exploring databases

Section topics	See also

The SQL Explorer (or Database Explorer in the Desktop edition of Delphi) lets you maintain a persistent connection to a remote database server during application development. With the SQL Explorer, you can:

Create and maintain database aliases

View schema data in a database, such as tables, stored procedures, and triggers

View table objects, such as fields and indexes

Create, view, and modify data in tables

Enter SQL statements to directly query any database

Create and maintain data dictionaries to store attribute sets


Flexible tool for storing objects as templates

Section topics	See also

You can store application objects such as forms, data modules, experts, and DLLs in Delphi's Object Repository so that you and others can easily reuse them. Borland has already stored a number of templates there for your use.

Use the Tools|Repository command to add objects to the Object Repository. Those objects are then available using the File|New command, which opens the New Items dialog box, shown here:

To add your own pages, right-click in the dialog box and choose Properties from the menu. This opens the Object Repository dialog box.


Project file-management tool

Section topics	See also

Use the Project Manager to easily keep track of and access the form and unit files that make up a Delphi application. Choose View|Project Manager to see a list of files in your application and easily navigate between files.

To view a unit, double-click it in the Unit column, or select it and press Enter.

To view a form, double-click it in the Form column, or select it and press Shift+Enter.


Handy pop-up menus

Section topics	See also

You can right-click (or left-click, if you've reconfigured your mouse) on objects and on several Delphi tools to view a menu of commands appropriate to the context. These are called “context menus.”

In the following figure, a panel object is selected. Right-clicking on the panel object displays the menu.


Toolbar buttons for frequently used commands

Section topics	See also

The Delphi toolbar, or SpeedBar, located to the left of the Component palette, contains buttons that perform common operations and commands (Open File, Save File, Add File To Project, and so on).

To find out what any button does, hold your mouse cursor over it for a moment. You can turn off these hints by right-clicking the SpeedBar and toggling off Show Hints. You can also use the right-click menu to hide the entire SpeedBar, making more room for the Component palette. To get the SpeedBar back, choose View|SpeedBar.


Getting help

Section topics	See also

Delphi's online Help provides the same documentation you'll find in the printed books that come with Delphi, plus more. For context-sensitive help in a dialog box, a window, or the Code editor, press F1.


See also

About the Component palette

Understanding components

Setting component properties

Working with event handlers

Working with the Code editor

Customizing the editor

Using the integrated debugger

Browsing and designing with the SQL Explorer

Reusing data modules in the Object repository

Viewing and manipulating a project’s contents


First app--a brief tutorial

Section topics	See also

This tutorial guides you through the creation of a small Delphi program that lets you navigate the fields in a marine-life database table that's supplied with Delphi. After you set up access to the table, you'll write an event handler that opens the standard File|Save dialog box. This allows you to write information from the database table to a file.


Starting a new application

Section topics	See also

Before beginning any new application, you should create a folder to hold the application's source files. This way, you don't mix your application source files with other types of files, and a unique folder lets you easily track and maintain all the files contained in your application.

1.	Create the folder MySource off the root directory of your working drive to hold the project files you'll create with this simple application.

2.	Open a new project.

Each application is represented in Delphi by a project. When you start Delphi, it opens a blank project by default. If another project is already open, choose File|New Application to create a fresh project.

Whenever you open a new project, Delphi automatically creates the following two files:

UNIT1.PAS: a source file associated with the main project form.

PROJECT1.DPR: a source file that keeps track of the forms in your project.

3.	Choose File|Save All to save your project files to disk. When the Save dialog appears, navigate to your MySource folder, and save the two Delphi files under their default file names. (Note that you can also use the Delphi Save As dialog box to create the new MySource directory.)

In addition to the two files already mentioned, Delphi creates other files associated with your project, as you can see by looking at your MySource directory. (These files are described in "What is a project?")

When you open a new project, Delphi displays a graphical representation of the project form, named Form1 by default:

The default new form has Maximize and Minimize buttons, a Close button, and a Control menu. If you run the form by pressing F9, you’ll see that these buttons all work. To return to design time, close the form.

In Delphi, you design the user interface for your applications using forms. Forms can contain menus and context menus, they can be put together to make application dialog boxes, and they can be parent or child windows. Essentially, forms are the canvases on which you create applications.

You place objects on forms to create user interfaces. Objects, for example, can be standard interface controls (such as check boxes and drop-down lists), or they can be fully contained components (such as data grids, bar charts, and editors).

In addition to the form, Delphi also displays the Object Inspector. While not an actual part of your application's interface, the Object Inspector shows you the design-time settings of all the objects you've placed in your project.

In the following figure, the drop-down list at the top of the Object Inspector shows the currently selected object; in this case, the object is Form1 and its type is TForm.

When the project form has focus, the Object Inspector shows the properties of the form.


Setting a property value at design time

Section topics	See also

To design the look of your application interface, you set the values of object properties using the Object Inspector. Setting property values in the Object Inspector while putting together your application interface is known as making design time settings.

Set the Color property of Form1 to clAqua.

To set the Color property, find the form's Color property in the Object Inspector and click the drop-down list displayed to the right of the property. To change the background color of the form to aqua, choose clAqua from the list of predefined colors (see the previous figure).

Note:	Using your programming environment to initialize the values of program objects is a notion that's new to many Delphi programmers. When programming with Delphi, you should make every effort to initialize object property values using the Object Inspector and its many property editors; resist the urge to set initial property values directly in the source code of the application. This way, you let Delphi set up and maintain the overall structure of your program objects and source code.


Adding objects to the form

Section topics	See also

The Delphi Component palette (see the following figure) lists many of the available components that you can use to create your application. Components are grouped onto different palette pages for easy access. You can look through the different pages of the palette by clicking the corresponding Component palette tabs.

In the following figure, the SpeedBar has been hidden to allow more room for the palette:

Using the components on the Component palette, you can quickly create an interface for your application.

1.	Add a Panel component to your form by double-clicking the Panel component on the Standard page of the Component palette.

To find the Panel component, point at a component in the Component palette for a moment; Delphi displays a Help Hint showing the name of the component, as shown in the following figure. When you find the Panel component, double-click it; Delphi adds that component to the middle of the current form.

Finding the Panel component

Placing a component on a form creates an object instance of that component. Although it might seem like a technicality, it helps to understand the difference between a Delphi component and an object that you are using in your application. Once you place a component on a form, Delphi generates all the Pascal code necessary to create that object in your application. Here lies the real beauty of Delphi; you don't have to worry about the code that creates or maintains the objects you use in your application--Delphi does all that work for you.

2.	Set the Panel object's Align property to alBottom.

Giving the Panel object focus on the form gives it focus in the Object Inspector. Setting the Align property to alBottom in the Object Inspector makes the panel lie flat across the bottom of the form.

3.	Enlarge the size of you application's window by dragging the lower right corner of the form.

4.	Enlarge the size of the panel until it fills the bottom third of the form (click the panel to select it, then drag its top border up). Your form should now resemble the one shown in the following figure:

Form with a Panel object

5.	Drop a Table object on the form.

Click the Data Access tab on the Component palette to access the components on that palette page. You can find the Table component on the left side of the page. When you find the Table component, click it once to select it, then click on the form to drop the component onto the form.

When you drop the table on the form, Delphi names the object Table1 by default.

6.	Set the DatabaseName property of Table1 to DBDEMOS. (DBDEMOS is the alias to a database that contains the table you're going to use.) You can select DBDEMOS from DatabaseName property drop-down list.

Setting this property value sets up access to a database table.


Accessing a database

Section topics	See also

You're now ready to set up the data source for your database controls.

1.	From the Data Access page of the Component palette, drop a DataSource component on the form. Set its DataSet property to Table1.

2.	From the Data Controls page, choose the DBGrid component and drop it into Panel1 (click the DBGrid component to select it, then click inside Panel1 to drop the component there). Your form should now resemble the following figure.

Form with a DBGrid object

The Data Controls page on the Component palette holds the components that let you control how you view database data in your applications. To display all the fields in a database table, use a DBGrid component.

The Table and DataSource objects don’t show when the application is running.

Set the following DBGrid properties to align the grid and to access the database you've set up with the Table and DataSource objects:

Set the Align property to alLeft, then drag the right edge of the grid so it fills three-quarters of the panel.

Set the DataSource property to DataSource1.

Now you can finish setting up the Table1 object you previously dropped on the form.

3.	Give the Table1 object focus by clicking on it in the form, then set its properties as follows:

Set TableName to BIOLIFE.DB.

Set Active to True.

When you set the Active property to True, the grid fills with the data contained in the BIOLIFE.DB database table. (If the grid doesn't fill as shown in the following figure, make sure you've correctly set the properties for all of the application objects as explained in the previous instructions.)

Form with an Active Table1

Since the DBGrid control is data aware, it displays the data in the table while you are designing your application. The data display gives you a visual check, showing that you're correctly hooked up to the database. However, note that being data aware doesn't mean you can edit the data at design time. To edit the data in the table, you'll have to run your application.

4.	Press F9 to compile and run the project.


Adding some spice

Section topics	See also

When you run the application, Delphi opens the program in a window just like the one you designed on the form. Your program is a full-fledged Windows program, complete with Minimize and Maximize buttons, and a Control menu that you can access from the upper left corner of the window. You'll see the data grid fill with the values from the table and you can scroll the data grid to see all the values in the BIOLIFE.DB table.

Even though your program contains all this, it's still lacking a few details. Double-click the Control menu to close the application and return to the design-time view of the form.

1.	From the Data Controls page of the Component palette, drop a DBNavigator object onto the form (as shown in the following figure).

2.	Set its DataSource property to DataSource1.

Form1 with a DBNavigator object

By default, the DBNavigator contains more controls than you'll need for this small application; for now, you only need the First, Prior, Next, and Last navigator buttons. The VisibleButtons property of the DBNavigator controls which navigator buttons display.

3.	With the DBNavigator in focus, double-click its VisibleButtons property label in the Objector Inspector.

The enumerated list of VisibleButtons expands in the Objector Inspector. Each of the expanded VisibleButtons properties corresponds to a button on the DBNavigator. These properties contain Boolean values that can be set to either True of False, where a False value turns off the display of the button.

4.	Set to False the following VisibleButtons properties (double-clicking the button property toggles the value from True to False):

nbInsert	nbDelete	nbEdit
nbPost	nbCancel	nbRefresh
5.	From the Standard page of the Component palette, drop another Panel component onto the top of the form. Delphi names this Panel2 by default. To remove this caption from your running application, clear the Panel2 string from the panel's Caption property.

6.	Align Panel2 to the top of the form by setting its Align property to alTop. Next drag the bottom of the panel down so it fills the top portion of the form.

7.	Set the color in both panels to clBlue.

Delphi lets you set property values in several components at the same time. First click on Panel1, then Shift+Click Panel2. The Object Inspector now shows all the properties that are common to both components. From here, you can set the Color property of both panels to clBlue.

8.	From the Data Controls palette page, drop a DBImage component on top of Panel2 and size it so your form resembles the one shown in the following figure by setting its Align property to alRight and dragging out the left side of the image.

Form1 with a DBImage object

You can drag to set the width of DBImage, or you can set its Width property in the Object Inspector.

9.	Set the following DBImage properties:

Set DataSource to DataSource1.

Set the DataField property to Graphic (the drop-down list shows the fields in the Biolife table; Graphic is one of the field names).

Again, because the DBImage component is data aware, the component displays the image of the fish in the first record of the table. This shows that you are indeed correctly hooked up to the database.

1. 	Click the Run button (the arrow) on the SpeedBar to compile and run your application.


Final touches

Section topics	See also

Now when you run the application, you can easily move through your database table using the buttons on the DBNavigator. However, you can add a couple of other touches to make the application a bit more useful. Close the running application to return to the design mode of Delphi.

1.	From the Data Controls page of the Component palette, drop a DBMemo component onto Panel2 and position it so it occupies the top left corner of the panel. Next, set the following property values:

Set DataSource to DataSource1.

Set DataField to Notes (use the drop-down list of fields).

Set ScrollBars to ssVertical.

2.	Drop a DBText object on Panel2 under the DBMemo object. Enlarge the DBText object so it fills the area under the DBMemo, then set its properties as follows:

Set DataSource to DataSource1.

Set DataField to Common_Name.

3.	Customize the Font property of the DBText object using the Font editor.

You can access several different types of property editors through the Object Inspector. For example, you can use the Menu editor, the Font editor, and the Picture editor to edit form menus, label fonts, and bitmap pictures and glyphs, respectively.

When you click the Font property of the DBText object, Delphi displays an ellipse button on the right side of the property setting, indicating that you can use a property editor to set this property. Clicking the ellipse displays the Font editor, a dialog box that lets you edit several characteristics of fonts.

Modify the following DBText settings using the Font editor, then click OK when you have finished:

Set the Font Style to Bold.

Set the Color to Silver.

Set the Size to 12.


Adding an Exit button to your application

Section topics	See also

Your application is now beginning to look like something. To make it a bit easier to use, add an Exit button so that it's obvious to the user how to close the program.

1.	Drop a BitButton (BitBtn) component onto Panel1 from the Additional page of the Component palette, then set its Kind property to bkClose.

This sets default values of the button so that when you click in the running application, the application closes.

2.	Change the Caption property of the BitButton so that it reads E&xit.

The ampersand before the x turns this character into a shortcut key, which lets you exit the program by pressing Alt+X.

Your program should now look like this one:

3.	Again, compile and run your application by pressing F9.

After running your application, click the Exit button, or press Alt+X to return to design mode.


Hooking up an event handler

Section topics	See also

You now have a fully functioning Windows application that accesses a database table and displays images, memos, and individual data fields from the database. There's even an Exit button so you can gracefully shut down the application.

Up until now, however, you have not typed a single line of program code. And this is how it should be if you want to take complete advantage of the Delphi environment. By using the Object Inspector to set the design-time values of your object properties, you let Delphi maintain the code that it generates by itself. In other words, let Delphi do the initialization work and save your coding efforts for the event handlers, the code that makes your program perform the tasks in your application.

Your next programming feat will be to hook up an event handler to a button, a task you'll encounter often when designing user interfaces with Delphi. You'll program the button so that when clicked, an event handler will call the standard Windows File|Save dialog box, which lets you save information from the database into a file.

1.	From the Dialogs page of the Component palette, drop a SaveDialog object onto the form next to the DataSource1 object.

2.	From the Additional Component palette page, drop a BitButton (BitBtn) object above the Exit button, and set its following properties:

Set Caption to &Save.

Set Glyph to the predefined FILESAVE.BMP bitmap using the Picture editor, like this:

When you click the Glyph property, Delphi displays an ellipsis button on the right side of the property setting, indicating that you can use a property editor to set this property. Click the ellipsis button to access the Picture editor. In the Picture editor, click the Load button, and navigate to the following folder in the Delphi directory hierarchy:

<Delphi main directory>\Images\Buttons

In this directory, find and load the FILESAVE.BMP glyph. When you choose OK in the Picture Editor, this glyph appears on the left side of your Save button.

The user interface of your application is now complete. If you followed all the steps in this tutorial, your final creation should resemble this form:

Now comes the most important part of your Delphi programming: creating and writing an event handler. The easy part is creating the event handler; the technical programming comes into play as you write the actual Pascal code for the event.

3.	Double-click the Save button to create the default event handler for the button.

Whenever you double-click an object on a Delphi form, Delphi generates the code for that object's default event handler. The Edit window opens, with the cursor placed where the first line of code in the event handler should be written. In this case, Delphi generates the following event handler code:

procedure TForm1.BitBtn2Click(Sender: TObject);

begin

end;

Up until now, you've used the Object Inspector to set object properties. However, you can also use the Object Inspector to access and create object event handlers. By clicking the Events tab on the Object Inspector, you can see the available events for the object currently in focus on the Delphi form.

Most components on the Component palette have a default event. When you double-click an object on a form, Delphi creates the handler for the default event. For most components, the OnClick event is default; this is the event that is called whenever you click an object in a running application.

4.	Complete the event handler by adding the code that is shown between the begin and end block of the event handler:

procedure TForm1.BitBtn2Click(Sender: TObject);

var

 i: integer;

begin

 SaveDialog1.Title :=Format('Save info for %s', [DBText1.Field.AsString]);

 if SaveDialog1.Execute then

 begin

   with TStringList.Create do

   try

     Add(Format('Facts on the %s', [DBText1.Field.AsString]));

     Add(#13#10);

     for i := 1 to DBGrid1.FieldCount-3 do

       Add(Format('%s : %s',

         [DBGrid1.Fields[i].FieldName,

          DBGrid.Fields[i].AsString]));

     Add(Format(CRLF+'%s'+CRLF,[DBMemo1.Text]));

     SaveToFile(SaveDialog1.FileName;

   finally

     Free;

   end;

 end;

end;

Detailing the workings of this code is beyond the scope of this tutorial. What the code does, however, is call the File|Save dialog box (the SaveDialog1 object) when you click the Save button. When you specify a file name, your application writes data on the currently selected fish to that file.

5.	To run the finished application, press F9.

Congratulations! You have now completed your first Delphi application.

To save your new program to disk, choose File|Save Project As. To learn more about Delphi projects and the naming conventions that Delphi uses for its project files, refer to "Working with projects."


See also

Jump in

Understanding components

Setting component properties

Adding components to the form

Panels

Working with data sources

Working with tables

Working with grids

Using data controls

Working with field components

Understanding datasets

Buttons and similar controls

Working with event handlers


Working with projects

Section topics	See also

When you're working in Delphi, you're working on a project. These topics describe the files that make up a project and then gives how-to information on working with projects. The topics covered here include

What is a project?

Viewing and manipulating a project's contents

Navigating among project components

Saving projects and individual project files

Sharing objects

Specifying a default project, new form, and main form

Managing multiple project versions and team development

Compiling, building, and running projects


What is a project?

Section topics	See also

A Delphi project is a collection of all the files that together make up a Delphi application or dynamic-link library. Some of these files are generated as you work on a project at design time. Others are created when you compile the project source code.

To manage projects effectively, you need to understand the various files and file types that make up a Delphi project. In most cases, you don't need to deal with the individual files unless you want to. Although you can edit most of these files directly as source code or scripts, it's easier and more reliable to use the visual tools in Delphi.


Project file

Section topics	See also

Every Delphi project contains Object Pascal source code that Delphi compiles into the finished application or dynamic-link library. The central point for the project's source code is called the project file. Delphi updates this file throughout the development of the project.

A very simple project could contain nothing besides the project file, but more often a project is more complex, and the project file contains references to all the forms and units used by the project. When you load, save, or compile a project, Delphi knows which other files to act on by looking at the project file.

By default, Delphi project files have the extension .DPR (Delphi project), but there is nothing special about the extension. Only the contents of the file are important, and they must be valid Object Pascal code.

When you compile or run the project, Delphi's compiler produces either an executable file or a dynamic-link library on disk with the same name as the project file, but with the extension .EXE or .DLL, as appropriate.

Inside the project file

Caution:	Because Delphi maintains the project file, you should not normally need to modify it manually, and in general it is not recommended that you do so. Most changes you could make to the project file can also be made with the Project Manager, and doing so ensures that Delphi keeps all the project's files synchronized.

The main reason to view the project file is so you can see the units and forms that make up the project, and which form is specified as the application's main form. As you add forms and units to the project, you can see Delphi's updates of the project source code.

To open the project file, use either of these methods:

Choose View|Project Source.

Right-click in the Project Manager, and choose View Project Source.

The contents of the project file appear in a page in the Code editor. (When you're finished viewing the project file, close its page in the Code editor.)

Delphi generates the following source code for a default, blank project:

program Project1;	{ declares project identifier }

uses 	{ indicates units used by project... }

 Forms, 	{ ...including non-form units... }

 Unit1 in 'UNIT1.PAS' {Form1};	{ ...and form units }

{$R *.RES}	{ links in resource file }

begin	{ start of main program block }

 Application.Initialize;

 Application.CreateForm(TForm1, Form1);	{ auto-creates first form }

 Application.Run;	{ runs the application }

end.	{ end of main program block }

Project1 is the identifier for the project. Delphi also uses this identifier to name the project file. For example, if you save the project source code file under the name MYNEWAPP.DPR, the first line of the source code would be

program MyNewApp;

The reserved word program indicates that this project is an application. If the project were a dynamic-link library, the reserved word library would appear instead.

The uses clause tells the compiler which units to link into the project. Forms is the identifier of a standard unit used by all Delphi projects that use forms.

Unit1 is the unit identifier for another unit, which contains a form. 'UNIT1.PAS' represents the name of the file that contains the unit's source code. These names are identical and must remain so in order for your project to compile correctly.

The reserved word in tells the compiler where to find the source-code file for each unit. The comment {Form1} specifies the instance identifier for the form associated with this unit (this would not appear in the clause if this were not a form-associated unit.) This is the same as the Name property of the form. You use the Object Inspector to name the form, and Delphi maintains the name in the project file.

The $R compiler directive specifies that the file with the same base name as the project and the extension .RES should be linked into the project. The project's resource file contains such items as the project's icon image. For more information, search online Help for the Resource File Directive topic.

The begin..end block is the main source-code block for the project.

The Application.CreateForm statement loads the form specified in its argument. Delphi adds an Application.CreateForm statement to the project file for each form you add to the project. The statements are listed in the order the forms are added to the project. This is the order that the forms will be created in memory at runtime. If you want to change this order, do not edit the project source code. Use the Project|Options menu command. (For more information see "Setting project options.")

The Application.Run statement starts your application.

Each time you add a new form or unit to the project, Delphi adds it to the uses clause in the project source code file. For more information, see "Adding existing forms and units to a project."


Form files

Section topics	See also

Forms are a very visible part of most Delphi projects. Normally, you design forms using Delphi's visual tools, and Delphi stores a binary description of the designed forms in form files.

You can open a form file in the Code editor and modify a text version of the same data, or you can translate your form files to text versions for easier maintenance and version control. You can also switch back and forth between the form and text views of a form using the View As Text and View As Form options on the context menus of the form or the Code editor, respectively.

Each form in a Delphi project also has an associated unit. The unit contains the source code for any event handlers attached to the events of the form or the components it contains. A unit associated with a form is sometimes called a form unit.

When you save a form unit or a project containing unsaved forms, Delphi prompts you to enter a name for each unit, which it uses as the name of the unit file, appending the extension .PAS. The form file gets the same name, but with the extension .DFM (Delphi form). You can use any extension you want on your unit files, but Delphi expects the .DFM extension on the corresponding form file.


Unit files

Section topics	See also

Delphi's Object Pascal language supports separately compiled modules of code called units. Using units promotes structured, reusable code across projects. The most common units in Delphi projects are form units, which contain the event handlers and other code for the forms used in Delphi projects. But units don't have to have forms associated with them. You can create and save a unit as a standalone file that any project can use. For example, you can write your own procedures, functions, DLLs, and components, and put their source code in a separate unit file that has no associated form.

If you open and save a default new project, the project directory initially contains one unit source-code file (UNIT1.PAS), and its associated form file (UNIT1.DFM).

When you compile or run the project or perform a syntax check on the project, Delphi's compiler produces an intermediate output file on disk from each unit's source code. By default the compiled version of each unit is stored in a separate binary-format file with the same name as the unit file, but with the extension .DCU (Delphi compiled unit). You should never need to open these binary files, and you do not need to distribute them with the completed project. The compiled-unit format is specific to the Delphi compiler, and enables rapid compiling and linking.

Note:	As an option, you can choose to have the compiler generate standard Intel object files (with the extension .OBJ) for greater compatibility with other compilers, but this greatly reduces the speed of compiling and linking your project. It should have no effect on the quality of the final generated code, however.

Unit files for forms

Most unit files you'll work with will probably be associated with forms. Whenever you create a new form, Delphi creates the corresponding unit file with the following code. The default unit identifier is incremented (Unit2, Unit3, and so on) for each new form.

unit Unit1;	{ unit identifier }

interface

uses	{ uses clause }
  SysUtils, Windows, Messages, Classes, Graphics, Controls,
  Forms, Dialogs;

type

 TForm1 = class(TForm)	{ class declaration }

 private

   { Private declarations }

 public

   { Public declarations }

 end;

var

 Form1: TForm1;	{ instance declaration }

implementation

{$R *.DFM}	{ compiler directive to link form file }

end.

The type declaration (or class declaration) part introduces the form as a class. A class is simply an object, which you will recognize if you are familiar with previous versions of Borland Pascal products, or another object-oriented programming language. "Programming with Delphi objects" discusses classes and objects in more detail.

The default type declaration makes the new form a descendant of the generic form class, TForm. This means it contains all the behaviors and characteristics of a TForm object.

The variable declaration declares your form as an instance of the class TForm1.

The $R compiler directive links the TForm's binary form file. This adds the form file(s) in your project to the compiled executable.

Caution:	Do not remove the {$R *.DFM} directive from a form unit file. Doing so will result in code that will never work correctly.

Unit files for procedures and functions

You can write custom procedures or functions within a unit that's associated with a form. However, if you want to reuse the routines that you write, it's better to create a separate unit to contain those routines. By creating standalone units that have no associated forms, you can easily make your procedures and functions available to other projects.

To create a unit file not associated with a form:

1.	Choose File|New.

2.	Choose Unit in the New Items dialog box.

3.	Choose OK.

It is not necessary to have a project open unless you want the new unit to be part of a project.

See "Writing Object Pascal code" for more information about units. Units are also used when you create new components, as described in the Developer's Guide.


Resource files

Section topics	See also

Delphi uses standard Windows-format resource files to include items such as the application icon in a project. You can edit these resource files (or create your own) to add other resources, such as bitmaps, cursors, icons, or strings to the application.

By default, each Delphi project that compiles into an application has a resource file with the same name as the project file, but with the extension .RES.


Package files

Section topics	See also

If you choose, you can use packages in your projects. Packages are special DLLs used by Delphi. They are explained in "Using packages." Packages have file extensions of .DPL (Delphi package library) and package source files have the extension .DPK (Delphi package). When you rebuild any project that contains a package, the package is implicitly recompiled, if necessary.


Other files

Section topics	See also

Delphi generates other files in conjunction with maintaining and compiling your project, most of which you never need to consider. These include:

Project options file

The project options file is a text file containing the current settings for project options, such as compiler and linker settings, directories, conditional directives, and command-line parameters. In most cases, you'll set these options using the Project Options dialog box (Project|Options), but Delphi saves them in text form for easy maintenance, version control, and sharing.

The project options file has the same name as the project file, but with the extension .DOF (Delphi options file).

Desktop settings

This text file stores information about the state of your project, such as which windows are open, and in what positions, so you can restore your workspace on a project-by-project basis.

The desktop-settings file has the same name as the project file, but with the extension .DSK.


Naming unit and project source code files

Section topics	See also

As you open new units in a project, Delphi gives them default names: UNIT1.PAS, UNIT2.PAS, UNIT3.PAS, and so on. You'll want to change a unit's default name to a meaningful name when you save the project. Use a name not previously used by any of the unit files already in the project file.

Delphi also supplies a default name for the project file (PROJECT1.DPR) which you should rename to something more meaningful the first time you save the project.

All unit and project file names must be legal Object Pascal identifiers. When the compiler looks for a unit or project file, it first searches for a file with the full name of the unit or project identifier. If it does not find that file, it will then search for a version of the identifier name, truncated to eight characters. This is for backward compatibility and for compatibility with file servers that store only short file names. You should not manually truncate your file names.


Storing a project

Section topics	See also

You should store each project in its own directory. Projects can share forms, files, and resources located in almost any directory, but it's best to keep the central project file and any other files specific to the project in a dedicated directory. See "Sharing objects" for more information about project templates and shared forms.


Viewing and manipulating a project's contents

Section topics	See also

Use the Project Manager to see information about the status and file content of the currently open project and to have a convenient way to open, add, save, and remove project files (you can do these tasks from the File menu as well). The Project Manager lets you easily navigate among the project's constituent files and gives you access to project options through its SpeedBar and context (right-click) menu.

To open the Project Manager window:

1.	Open a project.

2.	Choose View|Project Manager.

The following figure of the Project Manager shows files in the MDI Application, which is available from the Projects page of the New Items dialog box (File|New), plus one added shared file.

The Project Manager

To view a unit, double-click it in the Unit column, or select it and press Enter.

To view a form, double-click it in the Form column, or select it and press Shift+Enter.

If you share files among different projects, using the Project Manager is highly recommended because you can quickly and easily tell the location of each file in the project. This is especially helpful to know when creating backups that include all files the project uses. (See "Creating a backup of an entire project" for more information.)


Updating the Project Manager contents list

Section topics	See also

The Project Manager shows you all the files associated with the current project. These files are listed in the uses clause of your .DPR file. If you have modified this file manually (that is, you edited the source code directly), the information in this list might be inaccurate. When you open the Project Manager, Delphi compares the information in the project file to the last saved information for the Project Manager. If these are not synchronized, the contents of the Project Manager file list become dimmed, and the Update button on the SpeedBar becomes enabled so that you can synchronize the information.

Caution:	Delphi has mechanisms for automatically tracking the files that make up a project and for keeping the project file updated. Avoid editing project files manually unless you have a thorough understanding of this process and its ramifications. By editing a project file, you circumvent Delphi's automated project management, and you risk maintaining inaccurate information about project components. Compilation failures and other problems can result.


Getting project path and unit information

Section topics	See also

The status bar at the bottom of the Project Manager window displays the full path name of the project file, and indicates the number of forms and units in the project.

The project file path name can be a useful reference if you are bringing many forms and units that reside in locations other than the main project directory into the current project. The form and unit summary information can be helpful in evaluating the scope of proposed project modifications.


Using Project Manager commands

Section topics	See also

Here's what the Project Manager SpeedBar buttons (and comparable menu commands) do, with comments on each.

Button	context menu command	Delphi menu command	Function	Comment
Add Unit	Add File	File|Add To Project	Adds a shared or non-shared file to the project. The Path column of the Project Manager's file list reflects the location of any shared file.	Non-shared files reside in the project directory. Shared files still reside outside the project directory; they are not copied to the current project directory. Any changes to the shared file, in any project, are reflected in all projects using the file.
Remove Unit	Remove File	File|Remove From Project	Removes the selected file(s) from the current project.	Only the relationship of the selected file(s) with the current project is removed. The file still exists in its current location. The project file is updated to reflect the change.
View Unit	View Unit	View|Units (Ctrl + F12)	Opens and displays the selected file in the Code editor. If multiple files are selected in the Project Manager window, displays the most recently selected file.	Using the Delphi menu command opens the View Unit dialog box, where you can select which unit to open.
View Form	View Form	View|Forms (Shift + F12)	Displays the visual image of the currently selected form unit. If the selected file is not a form unit, this button and the context menu command are disabled.	Using the Delphi menu command opens the View Form dialog box, where you can select which form to display.
Project Options	Options	Project|Options	Displays the Project Options dialog box.
Update	Update		Synchronizes the Project Manager window display with listings in the project source code file.	This button normally remains disabled, and its parallel context menu command dimmed, unless you have manually modified the project file (which is not recommended).


Adding existing forms and units to a project

Section topics	See also

A project can use existing form and unit files that it did not create and that don't reside in the current project directory or any of its subdirectories. This includes custom Object Pascal procedures and functions that have been written as standalone routines. The table in "Using Project Manager commands" shows how to use the Project Manager to add such files to your project.

When you compile your project, it does not matter whether the files that make up the project reside in the project directory, a subdirectory of the project directory, or any other location.

If you add a shared file to a project, bear in mind that the file is not copied to the current project directory; it remains in its current location. Adding the shared file to the current project simply registers the file name and path in the uses clause of the project's .DPR file. Delphi automatically does this as you add units to the project. The compiler treats shared files the same as those created by the project itself.

Using Borland Pascal source code units

If you have existing source code units for custom procedures or functions written in Borland Pascal or Turbo Pascal, you can use these units in a Delphi project. You add these files in the same way as files created in Delphi.


Removing forms and units from a project

Section topics	See also

You can remove forms and units from a project at any point during project development. The removal process deletes the reference to the file from the uses clause of the .DPR file. Using the procedures in the table in "Using Project Manager commands" to remove a unit that has an associated form also removes the form from the project.

Removing a file from the project ends its relationship with the project; it does not delete the file from disk.

Caution:	Do not use Windows file management programs to delete Delphi project files from disk until you have performed the preceding removal process in every Delphi project that uses the files. Otherwise, the project file of each project using the deleted files retains references to them. When you open the project again, Delphi will attempt to find the deleted files and display error messages for each file it cannot find. When the project opens, the information about its constituent files in the Project Manager is inaccurate.


Using the Project Manager context menu

Section topics	See also

Right-click in the Project Manager to use the context menu:

Highlight a menu command, and then press F1 for information on that command.

When you create a unit in an open project with the New Unit command, the new unit is added to the uses clause in the .DPR file. When you create a new form with the New Form command, a blank form and a new unit are added to the project and added to the uses clause in the .DPR file.


Navigating among project components

Section topics	See also

As you work on a project, you will find that you frequently need to navigate back and forth among forms, units, and the various open windows such as the Project Manager, Alignment palette, and so on.


Toggling between form image and unit source code

Section topics	See also

To switch between viewing the current form and its unit source code, use any of the following methods:

Press F12.

Choose View|Toggle Form/Unit.

Click the Toggle Form/Unit speed button on the Delphi SpeedBar.

In the Project Manager, double-click a unit in the Unit column if you want to display the form's source code, or double-click a form in the Form column to display the form's image.


Bringing a window to the front

Section topics	See also

If you have a number of windows open, and you want to bring one of them to the front:

1.	Choose View|Window List or press Alt+0 (zero).

2.	Double-click the name of the window you want to bring to the front.

The Window List dialog box

Viewing forms and units

If you want to see just a list of forms or just units, choose View|Forms or View|Units. Choose a form or unit from the dialog box that appears, and choose OK.


Saving projects and individual project files

Section topics	See also

At any time during project development, you can save an open project in its current state to the project directory. You can optionally save a copy of the project in a different directory under the same or a different name.

You can also add your project to the Object Repository so that you or others can use it as a template. For more information, see "Adding items to the Object Repository."

You are not limited to saving a project as a whole; you can save individual constituent files of a project, including saving a copy of a file to a different directory or under a different file name.


Saving a project

Section topics	See also

Note:	If the project was begun from a project template, the Object Repository selection process creates the project directory. Otherwise, Delphi saves projects by default to the \BIN directory, unless you specify otherwise.

To save all open project files to the project directory, use one of the following methods:

Choose File|Save All.

Choose the Save Project button on the Delphi SpeedBar.

Right-click in the Project Manager, and choose the Save Project command on the context menu.

From here, the save process for projects varies somewhat depending on whether you have previously saved the project:

If you have not previously saved the project, Delphi displays the Save Unit As dialog box. This dialog box prompts you to supply a name for each open unit file that has been created in the current project. (You are not prompted for any shared files you might have added to the project. See "Sharing objects.")

After you name the unit file(s), Delphi prompts you to name the project file before saving it to disk. This processing order ensures that the unit and form file names you just specified are correctly registered in the project file's source code.

If you have previously saved the project, all open files that reside in the project directory are saved to disk if they have been modified.

If you have created any new forms or units in the project since the last save, the Save Unit As dialog box appears and prompts you to name those unit files before saving them.

The project file is then updated to reflect any new units and any newly shared files that you have specified for the project to use.


Saving a copy of the project

Section topics	See also

You can save a separate version of an open project in a directory other than the project directory by choosing File|Save Project As. However, because the open project might use shared files in addition to files that were created as part of the current project, the Save Project As command saves a copy of just the project file, project options file, and the project resource file to the new location.

Important:	No unit files are saved to the new location. When you open the copied version, the Project Manager displays all units in the copied project as shared files; that is, none of them reside in the directory you copied the project to.

To save a separate copy of the project file in another location:

1.	Choose File|Save Project As to display the Save <projectname> As dialog box.

2.	Select the directory where you want to save (copy) the project file.

3.	If you want to save the project file under a different name, enter the new name in the File Name edit box. If a project file with the same name exists in the directory you specify, you're asked if you want to overwrite the existing file.

4.	Choose OK.

The open project is now the project you just saved.

In addition to saving the project file, project options file, and project resource file to the new name/location, Delphi also saves any modified unit files (in their current location), therefore you won't be prompted to save these changes again when you close the project. When you open either version of the project, all changes saved with the Save As operation are reflected in both places.

If you check the file list in the Project Manager, you will see that all the files in the currently open version of the project reside in a directory other than the current project directory. If you want separate copies of any of those files in the new project directory, you need to save them individually to the new location using File|Save As. (See "Creating a backup of an entire project" for more information.)

If you leave the new project unchanged, it continues to use the files in their present (that is, old) location as shared files, which might or might not be what you want. If you don't understand how the new project is using its files, you can run into problems later.

Caution:	Do not use file management tools other than those in Delphi to save a copy of a project to a new location.


Saving individual files

Section topics	See also

You can save individual files in a project, or non-project files (such as text files) that you might have open in the Code editor. To save a file, it must be open.

To save an individual file:

1.	Bring the file to the front of the Code editor by selecting its tab.

2.	Choose File|Save File. If this is the first time you've saved the file, you're prompted to name it.

3.	If necessary, name the file and choose OK.

Delphi saves the file.

Note:	Each file must have a unique prefix, even if their extensions are different. For example, if you specify About as the file name for both the form and project file, Delphi displays the following error message:

The project already contains a module named About.

If you see this error message, Delphi won't save the project file (or whichever file you named secondly). Save the file again with a different name.

To save a file under a different name or location:

1.	Bring the file to the topmost level of the Code editor by selecting its tab.

2.	Choose File|Save File As.

The Save File As dialog box appears.

3.	Specify the new file name, or location, or both, and choose OK.

Delphi saves a copy of the file under the name and location you specify.

Note:	This changes the name of the file, and if it is already part of the project, includes the file with the new name in the project. The older file still exists, but isn't included in the project any longer.


Creating a backup of an entire project

Section topics	See also

Backing up a project can be a simple matter of copying directories or can involve some additional steps. This depends on how your project directories are structured and whether the project uses files from outside its own directory tree.

The project directory isn't encoded into the project file. The project file does, however, record the location of all the other files in the project. If these files reside in subdirectories of the main project directory, all path information is relative, which makes backup easy. You could back up such a project by copying (not moving) the directory tree to another location. If you open the project at the backup location, all the project files that reside within that structure are present, and the project will compile.

If this project uses files that reside outside this tree, the project might or might not compile at the backup location. Check the Project Manager's file list to see if these outside files are accessible from the backup location. If they are, the project will compile. If other backup processes already preserve these outside files, then there is probably no need to make separate backup copies of them in the backup project directory.


Sharing objects

Section topics	See also

Delphi's Object Repository (Tools|Repository) is a versatile tool that makes it possible to easily share (or copy) forms, dialog boxes, and data modules across projects and within a single project. It also provides project templates as starting points for new projects. By adding forms, dialog boxes, and data modules to the Object Repository, you make them available to other projects. In fact, you can add an entire project to the Object Repository as a template for future projects.

You'll also see wizards in the Object Repository. Wizards are small applications that lead the user through a series of dialog boxes to create a form or project. Delphi provides a number of wizards, and you can also add your own.

The repository is actually a text file named DELPHI32.DRO (Delphi repository objects) in the DELPHI 3.0\BIN directory that contains references to the items that appear in the Object Repository dialog box and the New Items dialog box. You can open this file in any text editor.


Sharing items within a project

Section topics	See also

It's also easy for you to share items within a project without having to add them to the Object Repository: when you open the New Items dialog box (File|New), you'll see a page tab with the name of your project. If you click that page tab, you'll see all the forms, dialog boxes, and data modules in your project. You can then derive a new item from an existing item, and customize it as needed.


Sharing objects in a team environment

Section topics	See also

To share objects in a team environment, you need to specify a directory that's available to team members. After you do this, another DELPHI 32.DRO file is created in the specified directory as soon as you add an item to the Object Repository. The new DELPHI32.DRO text file contains pointers to the objects you want to share.

To specify a shared repository directory:

1.	Choose Tools|Environment Options.

2.	On the Preferences page, locate the Shared Repository panel.

3.	In the Directory edit box, enter the name of the directory where you want to locate the shared repository.

The location of your shared directory is stored in the Windows registry. Changing the location in the Environment Options dialog box changes it in the registry as well.

To share Object Repository items among team members, every member's Directory setting in the Environment Options dialog box must point to the same location.


Copy, Inherit, or Use?

Section topics	See also

To gain access to items in the Object Repository, choose File|New. The New Items dialog box appears, showing you all the items in the Object Repository. You have three options for adding an item to your project:

Copy

Inherit

Use

The Copy option

Select Copy to make an exact copy of the selected item and add the copy to your project. Future changes made to the item in the Object Repository will not be reflected in your copy, and alterations made to your copy will not affect the original Object Repository item.

Copy is the only option available for using project templates.

The Inherit option

Select Inherit to derive a new class from the selected item in the Object Repository and add the new class to your project. The Inherit option creates a link to the ancestor item in the repository. When you recompile your project, any changes that have been made to the item in the Object Repository are reflected in your derived class. These changes apply in addition to any changes or additions you make to the item in your project. Changes made to your derived class do not affect the shared item in the Object Repository.

Inherit is available as an option for forms, dialog boxes, and data modules, but not for project templates. It is the only option available for reusing items from within the same project.

The Use option

Select Use when you want the selected item itself to become part of your project. In this case, you are not making a copy of the item; you are using the item itself, "live." Using an item is like reverse inheritance: instead of inheriting changes others make to an item, they inherit your changes when they use the item in the repository. Changes to the item appear in all projects that have added the item with the Inherit or Use options selected.

Caution:	The Use option is available for forms, dialog boxes, and data modules, but you should use it carefully. Make sure the changes you make to an item are thoroughly tested before letting others copy it into their applications from the repository.

Note:	The Use option is the only option available for experts, whether form experts or project experts. Using an expert doesn't actually add shared code, but rather runs a process that generates its own code.


Using project templates

Section topics	See also

Project templates are predesigned projects you can use as starting points for your own projects.

To start a new project from a project template:

1.	Choose File|New to display the New Items dialog box.

2.	Choose the Projects tab.

3.	Select the project template you want and choose OK.

4.	In the Select Directory dialog box, specify a directory for the new project's files. If you specify a directory that doesn't exist, Delphi creates it for you.

Delphi copies the template files to the project directory. You can then modify the project, adding new forms and units, or use it unmodified, adding only your event-handler code. In any case, your changes affect only the open project. The original project template is unaffected and can be used again.


Adding items to the Object Repository

Section topics	See also

You can add your own projects, forms, and data modules to those already available in the Object Repository.

To add an item to the Object Repository:

1.	If the item is a project or is in a project, open the project.

2.	For a project, choose Project|Add To Repository. For a form or data module, right-click the item and choose Add To Repository from the context menu.

3.	Type a description, title, and author.

The title will appear in the Object Repository window and in the New Items dialog box (File|New).

4.	Decide where you want this item to appear in the New Items dialog box, and select that page from the Page combo box. Or, type the name of the page.

If you type the name of a page that doesn't exist, Delphi creates a new page for you, and your new page name will appear on a tab of the New Items dialog box.

5.	Choose Browse to select an icon to represent the object in the Object Repository.

6.	Choose OK.


Modifying a shared form

Section topics	See also

If several projects share a form in the Object Repository, then modifications you make to the form can affect all projects, depending on how the form is imported into each project. If a project copies a form from the Object Repository, then later modifications to the form in the Object Repository have no effect on the project. If a project inherits a form from the Object Repository, then each time the project is compiled, it inherits the latest version of the form from the Object Repository, including any changes made since the project was last compiled. If a project "uses" a form from the Object Repository, then any time you make changes to the form in the project, the changes are stored in the Object Repository directly where other applications can copy or inherit them.

If you know that other projects inherit a form in the Object Repository, but you do not want to replicate your changes to those projects, there are several ways to prevent inheritance:

Save the form under a different name and use the renamed form in your project instead of the original.

Make the changes to the form at runtime instead of at design time.

Make the shared form a component that can be installed onto the Component palette. This has the added advantage of enabling users to customize the form at design time. For more information, see "Creating component templates."

If you expect to be the only user of the form, and you don't plan extensive or frequent changes, runtime customization is probably acceptable. If you plan on using the form in many different applications, runtime customization involves more coding for you and other developers. In this case, it's usually more convenient, whenever possible, to make the form a component that other users or developers can install onto their Component palette.

Note:	You can also reuse a form by making it into a DLL.


Specifying a default project, new form, and main form

Section topics	See also

You can specify defaults for a new project, a new form, and a main form. You always have the option to override the defaults by choosing File|New and selecting from the New Items dialog box.


Specifying the default new project

Section topics	See also

The default new project opens whenever you choose File|New Application. If you haven't specified a default project, Delphi creates a blank project with an empty form. You might want to specify a project you're using as a template to be the default new project.

You can also designate a project expert to run by default when you start a new project. A project expert is a program that enables you to build a project based on your responses to a series of dialog boxes.

To specify the default new project:

1.	Choose Tools|Repository to display the Object Repository dialog box.

2.	Choose Projects in the Pages list.

3.	Select the project you want as the default new project from the Objects list.

4.	With the project you want selected, check New Project.

5.	Choose OK to register the new default setting.


Specifying the default new form

Section topics	See also

The default new form opens whenever you choose File|New Form or use the Project Manager to add a new form to an open project. If you haven't specified a default form, Delphi uses a blank form. You can specify any form as the default new form. Or you can designate a form expert to run by default when a new form is added to a project.

To specify the default new form for new projects:

1.	Choose Tools|Repository to display the Object Repository dialog box.

2.	Choose Forms in the Pages list.

3.	Select the form you want as the default new form from the Objects list.

4.	With the form you want selected, check New Form.

5.	Choose OK to register the new default setting.


Specifying the default main form

Section topics	See also

Just as you can specify a form template or expert to be used whenever a new form is added to a project, you can also specify a form template or expert that you want to use as the default main form whenever you begin a new project.

To specify the default main form for open projects:

1.	Choose Tools|Repository to display the Object Repository dialog box.

2.	Choose Forms in the Pages list.

3.	Select the form you want as the default main form from the Objects list.

4.	With the form you want selected, check Main Form.

5.	Choose OK to register the new default setting.


Setting project options

Section topics	See also

You can change project settings in the Project Options dialog box. To open this dialog box, do one of the following:

Choose Project|Options.

In the Project Manager, choose the Options button on the SpeedBar, or right-click in the Project Manager, and choose Options from the context menu.

The settings you change affect only the current open project, unless you check the Default check box (see the next topic). If you change any of the default settings, an options file with a file extension .DOF (Delphi options file) is created in the project directory the next time you save the project. So when you reopen the project in future work sessions, the project options you set are in effect.

These topics discuss only those project options that pertain to project management. For detailed information on the options on any given page of the dialog box, click the Help button on that page.


Setting options that affect all new projects

Section topics	See also

The Project Options dialog box contains a check box labeled Default. Checking this control writes the current settings from the Compiler, Linker, Directories/Conditionals, Packages, and VersionInfo pages of the Project Options dialog box to a file called DEFPROJ.DOF. Delphi creates this file when you check the Default box and choose OK in the Project Options dialog box. Delphi then uses the project options settings stored in this file as the default for any new projects you create.

If you create a project from a template in the Object Repository that has its own options file, those settings will override the default settings in DEFPROJ.DOF.

Form options

The Forms page of the Project Options dialog box lets you change the form designated as the project's main form, and lets you control the creation order of forms at runtime. For information see "Specifying a form as the project main form."

Application options

On the Application page you can specify a title, Help file, and icon for the project. And you can specify these options as default project settings. (You can also read or set these items in code at runtime, using the Title, HelpFile, and Icon properties of the Application object.)

Title	This text will appear below the application icon when you minimize the running application. If no title is specified, the minimized application displays the file name of the .EXE file.
Help	If you create a Windows Help (.HLP) file for the project, you "connect" it to the application by specifying it in the Help File edit box. Either type a file name or use the Browse button to select an existing Help file.At runtime, the Help file name is used by the HelpCommand, HelpContext, and HelpJump methods of the Application object. The application's Help methods all call the Windows API function WinHelp, passing the name of the Help file.Caution: Be aware that the Help file for your distributed application might not reside in the same location, relative to the executable file, as it does on your development system. Avoid specifying a drive name unless you know for certain that the finished application's Help file will always reside on a drive with that name.Consult your Windows Help documentation for further information on how WINHELP.EXE attempts to locate Help files.
Icon	Delphi supplies a default icon for all projects, which appears whenever the running application is minimized, or when the application file appears in the Windows Explorer or Program Manager. You'll probably want to use a different icon for your applications. The Delphi Image Library supplies some icon files that you can use directly or modify by using the Delphi Image editor. You can specify any standard Windows icon file residing in any location. The specified icon is compiled into the project's executable file.
Compiler options

The Compiler page lets you specify compile options for code generation, syntax, runtime errors, debugging, and messages.

Linker options

The Linker page lets you specify how you want your program files to be linked, including options for linker output generation, and .EXE and .DLL options .

Check the Include TD32Debug Info check box if you want debugging information to be compiled into the project executable file. (This enlarges the size of that file.)

Directory and Conditional options

On the Directories/Conditionals page, you can control the location of the project's compiled output and specify the location of resources needed to compile, link, and distribute the application:

Output Directory	If you want the compiled project output to be stored in a directory other than the project directory, you can enter a path in the Output Directory edit box. The specified directory must exist or be accessible at compile time or Delphi generates an error message.
Unit Output Directory	Use this edit box to specify the directory where you want compiled unit files to be stored.
Search Path	The Search Path edit box specifies the location(s) of any non-Delphi library file(s) needed to compile the program. The Delphi compiler always includes the BIN and LIB subdirectories in its search for resources. If your program needs to access libraries residing elsewhere, you need to specify the library path location in this area of the dialog box. See online Help for more information.
Debug Source Path	Search path for the debugger.  The debugger searches paths defined by the compiler by default. If the directory structure has changed since the last compile, path name(s) can be entered here to include a file or files in the debugging session.
Conditional Defines	Enter any symbols referenced in conditional compiler directives in the Conditional Defines edit box. See online Help for information.
Unit Aliases	Useful for backwards compatibility.  Specify alias names for units that may have changed names or were merged into a single unit.  The format is <oldunit>=<newunit>.  You can separate multiple aliases with semicolons.  The default value is WinTypes=Windows;WinProcs=Windows.Default
Version Information

The VersionInfo page lets you specify whether to include version information in project builds and how you want it done, including build attributes. The information is included in a tag in your code, and when you right-click the compiled .EXE or .DLL file in the Windows Explorer (or on the desktop) and choose Properties from the menu, a page added to the Properties dialog box shows the version information you specified.

Packages

The Packages page lets you specify what design-time packages to use in your project and, optionally, what runtime packages you want included in your distributed application. Packages you list in the Runtime Packages edit box are automatically linked into your project when you compile. "Using packages" lists the packages that ship with Delphi: design-time packages and the Component palette pages affiliated with them; and runtime packages and the units they contain.


Restoring Delphi's original default settings

Section topics	See also

To restore Delphi's original default settings, delete, or rename the DEFPROJ.DOF file.


Setting environment preferences

Section topics	See also

In addition to changing project settings you can customize the Delphi environment (editor, designer, debugger, and compiler). Preferences you set in the Environment Options dialog box affect all Delphi projects.

To specify environment settings, choose Tools|Environment Options. Click the Help button on any page of the Environment Options dialog box for help with that page.

Note:	If you share your installation of Delphi with other users, it's possible that another user has modified the default option settings. In a shared-installation situation, it's a good idea to check environment options before creating a new project.


Managing multiple project versions and team development

Section topics	See also

When you are developing a complex programming project in a team setting, or managing several development projects, you might soon develop the need for a version control system (VCS). A version control system can archive files, control access to project files, and track multiple versions of your projects.

Delphi Developer has a built-in interface for Intersolv's PVCS Version Manager (available separately). It supports PVCS version 5.1 and later. Because Delphi uses an Open Tools API, it can also be used with any commercially available version-control utility. Delphi Client/Server Suite includes both the PVCS interface and PVCS itself.

Enabling team development support

Before using Delphi's built-in team development support, you need to have PVCS installed on your system, and the several files must reside in your \WINDOWS\SYSTEM directory.

For information on using the team development support features of Delphi, choose WorkgroupsIBrowse PVCS Projects to display the PVCS Project window, then click the Help icon to open the PVCS Help system for details on version control tasks.. You can also read the online Help for PVCS by opening the file PVCS.HLP.


Compiling, building, and running projects

Section topics	See also

All Delphi projects have as a target a single distributable executable file, either an .EXE or a .DLL file. You can view or test your application at various stages of development by compiling, building, or running it. You can also test the validity of your source code without attempting to compile the project.


Checking source code syntax

Section topics	See also

You can run a syntax check on your project's source code without compiling. The checking is identical to that for a compilation, and errors in your code generate the same error messages. However, the process is faster because Delphi makes no attempt to generate object code, and if the check succeeds, no executable file is generated.

To run a syntax check on your project source code:

1.	Save the project source code (recommended but not required).

2.	Choose Project|Syntax Check.

The hourglass cursor appears, to indicate that syntax checking is in progress and returns to normal if the check finds no errors. If an error is found:

The Code editor window comes to the front.

The unit source file page containing the error comes to the top of the Code editor.

The line containing the error is highlighted in the Code editor.

The Code editor message view displays an error message.

Context-sensitive help regarding the error message is available by pressing F1 when the error line is highlighted and has focus.

Syntax or compile error display:


Compiling a project

Section topics	See also

To compile all the source-code files that have changed since the last time you compiled them,
Choose Project|Compile.

When you choose this command, this is what happens:

The compiler compiles source code for each unit if the source code has changed since the last time the unit was compiled. This creates a file with a .DCU (Delphi compiled unit) extension for each unit.

If the compiler can't locate the source-code file for a unit, the unit isn't recompiled.

If the interface part of a unit's source code has changed, all the other units that depend on it are recompiled.

To learn about the interface section of a unit, see "Working with the Code editor."

If a unit links in an .OBJ file (a file containing assembly language code), and the .OBJ file is newer than the unit's .DCU file, the unit is recompiled.

If a unit contains an include (.INC) file, and the include file is newer than the unit's .DCU file, the unit is recompiled.

Once all the units that make up the project have been compiled, Delphi compiles the project file and creates an executable file (or dynamic-link library). This file is given an .EXE (or .DLL) file extension and the same file name as the project source code file. This file now contains all the compiled code and forms found in the individual units, and the program is ready to run.

You can choose to compile only portions of your code if you use {$IFDEF} conditional directives and predefined symbols in your code. For information about conditional compilation, see "Compiler directives" of this book or "conditional directives" in online Help.

Obtaining compile status information

You can get information about the compile status of your project by displaying the Information dialog box ( Project|Information). This dialog box displays information about the number of lines of source code compiled, the byte size of your code and data, the stack and file sizes, and the compile status of the project.

You can get status information from the compiler as a project compiles by checking the Show Compiler Progress box in the Environment Options dialog box, Preferences page. (For more information, see "environment options" in online Help.)


Building a project

Section topics	See also

To compile all the source-code files in your project, regardless of when they were last compiled,
Choose Project|Build All.

The result of this command is similar to that of the Project|Compile command, except that all units in the project are compiled, regardless of whether or not they have changed since the last compile. This technique is useful when you are unsure of exactly which files have or have not been changed, or when you simply want to ensure that all files are current and synchronized. It's also important to use Build All when you've changed global compiler directives, to ensure that all code compiles in the proper state.


Running a project

Section topics	See also

You can test run a project from within Delphi, or you can run the compiled .EXE file from the Windows operating environment without having to run Delphi.

To compile and then run your application from within Delphi, either:

Choose Run|Run.

Choose the Run button on the SpeedBar.

These actions are identical to choosing the Project|Compile command, except that Delphi runs your application immediately if the compile operation succeeds.

Executing a project from Windows

Because the compiler always creates a fully compiled standalone executable file (.EXE), you can run your application from the Windows operating environment using the same techniques as you would for any other Windows application.

If you have specified an icon for your project, it will appear beside the file name in the Windows Explorer, in a shortcut on the desktop, on the Windows Start menu, and on the Taskbar when you minimize the application while it is running.


See also

Compiler directives

The command-line compiler

Understanding Object Pascal units

Viewing files in the Code editor

Specifying a form as the project main form

Project Options dialog box

Environment Options dialog box


Understanding components

Section topics	See also

Using Delphi's visual design tools, you can create the visual elements of an application quickly, in a manner that promotes reuse of design elements, rather than repeated reinvention. And as you work with the design tools, Delphi writes the corresponding Pascal code for you.

At the simplest level, designing a user interface in Delphi consists of four steps:

1.	Creating forms

2.	Placing components on forms

3.	Setting component properties

4.	Attaching code to component events

These topics give you

Basic information on Delphi forms and components

Help in choosing the right component for the job

Instructions for customizing the Component palette


Forms, the basic building blocks

Section topics	See also

All your visual design work in Delphi takes place in forms. The forms you design become the windows and dialog boxes of your application. Some of the components that you place on a form are the controls that let a user operate the application. Other components you can put on your forms are application elements such as databases.

There are also specialized objects called data modules that are invisible when the application runs, since they hold non-visual components such as databases that the user of the application doesn't interact with directly.

Although it's often convenient to distinguish between forms and the components they contain, a form is a component, too. Because of this, you can set and read its properties, call its methods, and respond to its events.


Overview of components

Section topics	See also

Each component you put on a form becomes a user-interface object, a database object, or a system function, such as a system timer. By placing components on forms, you build the interface of your application.

Components are objects in the true object-oriented programming (OOP) sense. Because they are objects, Delphi components

Encapsulate some set of data and data-access functions

Inherit data and behavior from the objects they are derived from (called their ancestors)

Operate interchangeably with other objects derived from a common ancestor, through a concept called polymorphism

Although each component has its own unique aspects, all components share certain qualities they inherit from a common ancestor object called TComponent. TComponent defines the minimum set of attributes necessary for a component to operate in the Delphi environment.

The attributes common to all components are described in "Common component elements."

The important thing to understand about components at this point is that they contain three kinds of information:

State information. Information about the present condition of a component is called a property of the component. Properties are named attributes of a component that a user or application can read or set. Examples of component properties are Height and Color.

Action information. Components generally have certain actions they can perform on demand. These actions are defined by methods, which are procedures and functions you call in code to tell the component what to do. Examples of component methods are Hide and Refresh.

Feedback information. Components provide opportunities for application developers to attach code to certain occurrences, or events, making components fully interactive. By responding to events, you bring your application to life. Examples of component events are OnClick and OnKeyDown.


Delphi's standard components

Section topics	See also

The Delphi Component palette contains a large selection of components you can use in your Delphi applications. You can add, remove and rearrange components on the palette, as you choose. You can also create component templates that are made up of a number of components and add the templates to the Component palette. (See "Customizing the Component palette.")

The components on the palette are grouped into pages according to similar functions. The following table lists the default pages and the kinds of components they contain.

Page name	Contents
Standard	Standard Windows controls, menus
Additional	Customized controls
System	Components and controls for system-level access, including timers, file system, multimedia, and DDE
Win32	Windows 95/NT 4.0 common controls
Dialogs	Windows common dialog boxes
Data Access	Non-visual components that access databases, tables, queries, and reports
Data Controls	Visual, data-aware controls
Win 3.1	Components for compatibility with Delphi 1.0 projects. Also, the file control components on this page are useful for building custom dialog boxes that contain file controls when the common file dialogs are not appropriate.
Internet	Components that help manage client/server apps: components for various internet communications protocols; sockets for managing low-level TCP/IPs; objects for building Web server apps
Samples	Sample custom components, including gauge, color grid, spin buttons, directory outline, calendar grid, IBEvent Alerter
QReport	Quick Report components for easily creating embedded reports
Decision Cube	Controls that let you summarize information from databases and view it from a variety of perspectives, changing the focus of the analysis on-the-fly
ActiveX	Sample ActiveX controls

For general information on each component you see on the default palette, press F1 for each selected page of the Component palette.  To quickly access the Visual Class Library documentation for a component, drag it to a form, select it, and press F1.


Two views of components

Section topics	See also

As noted earlier, components encapsulate elements of applications in a standardized, reusable way. Understanding this component model is an important step in understanding Delphi.

There are a number of ways to categorize the components that come with Delphi. Each provides a different set of information that can help you use the components more effectively.

These topics examine two such views of Delphi's components: a hierarchical view and a functional view. The hierarchical view divides components according to what they are, where the functional view divides components according to what you use them for.


The component hierarchy

Section topics	See also

One way to understand components is to understand their relationships as objects. That is, to look at the inheritance relationships that different components share. By noting the common properties, methods, and events that various groups of components inherit, you can understand some of their similarities and differences.

This is not meant to be an exhaustive description of the complete hierarchy of Delphi components. Rather, it will acquaint you with some of the broadest categories of components. For more details on the component hierarchy, see the Visual Component Library Reference and the Developer's Guide.

This figure shows a simplified view of the major kinds of components you'll deal with in Delphi:

In general, there are two broad categories of components: nonvisual components and controls.

Nonvisual components are components that represent program elements that the user cannot interact with directly, such as system timers and database connections. Nonvisual components appear at design time as small icons on a form or data module, which enables you to select them for setting properties and attaching event handlers.

Some nonvisual components actually represent visual elements when the application runs, such as a main menu bar or a Windows common dialog box. In those cases, the nonvisual component represents the design-time interface for items that only exist at runtime; essentially they store a description of items the application will create at the appropriate time when running.

Controls are visible elements the user can interact with at runtime. In general, they look the same at design time as they will at runtime, which facilitates form layout. Whenever possible, Delphi controls look and act "live" at design time: a list box displays its list of items, a data grid connected to an active data set displays actual data.

There are two subcategories of controls that are useful to know about: windowed and graphical controls.

Windowed controls are controls that can get input focus. Most of the standard Windows controls are windowed controls. The term "windowed" comes from the fact that such controls have a window handle (which you can access through a Handle property).

Graphical controls (sometimes called "nonwindowed controls") are controls that cannot receive focus, and do not have window handles. Graphical controls consume fewer system resources, so they are useful for complex forms that need to display numerous controls.

The distinction between windowed and graphical controls can be important when you design your user interfaces. For example, if you create a tool bar that contains a large number of buttons, you could use standard Windows button components, but each one would consume a window handle. You could instead choose a nonwindowed speed-button component that would greatly reduce the drain on system resources.


Functional divisions

Section topics	See also

Another way to understand components is to look at what they do. By comparing components that perform related tasks, you can determine which components are best suited to a particular task. In many cases there are several components that can all perform the desired action, but you might choose one because it has greater capacity, better performance, or lesser impact on system resources.

The following table is not meant to be an exhaustive dissection of all the tasks you can perform with Delphi components. Rather, it spells out the categories of common user-interface tasks and suggests the components you would consider for those tasks. The topic "Choosing the right component" goes into more detail, comparing the different components.

Task category	Components	Topic
Text input and manipulation	edit box, memo, mask edit, rich edit, DBText, DBEdit, DBMemo, DBRichEdit	"Text controls"
Specialized input	scroll bar, track bar, up-down, hot key, splitter	"Specialized input controls"
Button input, choosing options	button, check box, radio button, bitmap button, speed button, DB check box, DB radio button, tab set, tool bar, cool bar, main menu, pup-up menu	"Buttons and similar controls"
List display and manipulation	list box, combo box, tree view, list view, DB list box, DB check box, DB lookup list box, DB lookup combo box, checklist box, date/time picker, DBComboBox	"Handling lists"
Grouping	group box, radio group, panel, scroll box, tab control, page control, header control, DBRadioGroup	"Grouping components"
Visual feedback	label, progress bar, status bar, panel, static text	"Visual feedback"
Tabular display	string grid, draw grid, DB grid, DB control grid	"Tabular display"
Graphic display	image, shape, bevel, image list, paint box, animate	"Graphic display"
Windows common dialog boxes	open dialog, save dialog, font dialog, color dialog, print dialog, printer setup dialog, find dialog, replace dialog, open picture dialog, save picture dialog	"Windows common dialog boxes"


Common component elements

Section topics	See also

All components have properties, methods, and events built into them. Some of these they inherit from their ancestor component types, which means they share these elements with other components. Such elements are called common elements. For example, all controls inherit a property called Height that represents the vertical size of the control. Height is therefore a common property of all controls.

Each type of component also introduces its own unique elements, called key elements. For example, the check box component has a property called Checked that indicates the state of the check. That property is unique to the check box, and is highlighted by a "key" symbol in the library reference and online Help.

The Name property

There are several properties common to all components, but the most important is the Name property. Name is different from other properties in several ways. Every component in an application must have a unique name, so assigning meaningful names at the outset not only makes your code more readable, but prevents possible name conflicts later.

Component names must follow the standard rules for naming Object Pascal identifiers. If you enter a value that is not consistent with Object Pascal naming requirements, the name reverts to its previous value, and Delphi displays the following error message:

"<name>" is not a valid component name.

For more information about Object Pascal naming conventions, see "Writing Object Pascal code."

Caution:	As long as you use the Object Inspector to change a component's Name property, Delphi maintains your changes in the underlying code. This is not the case, however, if you edit the component name yourself by typing the change directly into the Code editor. If you manually edit a component name, Delphi will be unable to load your form.


Choosing the right component

Section topics	See also

Delphi provides a large number of different components, many of which perform similar functions, but in specialized ways. These topics are designed to help you choose among those groups of similar components. First they describe those properties common to all visual components. Then they discuss different functional groups of components, how to choose among them, and briefly describe the properties that make each component in a group unique.


Properties common to all components

Section topics	See also

All visual components have a number of properties in common. Once you have an understanding of what is in every visual component, you can better evaluate the unique properties that define the various specialized components you'll use to create your applications.

The common properties fall into these general categories:

Position and size properties

Display properties

Parent properties

Navigation properties

Drag-and-drop properties

Position and size properties

Four properties define the position and size of a control on a form:

Height sets the vertical size

Width sets the horizontal size

Top positions the top edge

Left positions the left edge

These properties aren't accessible in nonvisual components, but Delphi does keep track of where you place the component icons on your forms.

Most of the time you'll set and alter these properties by manipulating the control's image on the form or using the Alignment palette. You can , however, alter them at runtime.

Display properties

Four properties govern the general appearance of a control:

BorderStyle specifies whether a control has a border.

Color changes the background color of a control.

Ctrl3D specifies whether a control will have a 3-D look or a flat border.

Font changes the color, name, style, or size of text. You can set aspects of the font, such as its size and color, individually, or you can use a Font dialog to change several aspects at a time.

Parent properties

To maintain a consistent look across your application you can make any control look to its container for display properties. Setting the parent property to True for ParentColor, ParentFont, or ParentCtrl3D will make display edits automatically update across all components on a form.

If you change Font, Color or Ctrl3D after you set the corresponding parent property to True, your color or font choice will take effect and the associated parent property will be set to False.

Navigation properties

Several properties determine how users navigate among the controls in a form:

Caption contains the text string that labels a component. To underline a character in a string, include an ampersand (&) before the character. This type of character is called an accelerator character. The user can then select the control or menu item by pressing Alt while typing the underlined character.

TabOrder indicates the position of the control in its parent's tab order, the order in which controls receive focus when the user presses the Tab key.  Initially, tab order is the order in which the components were added to the form, but you can change this by changing TabOrder. TabOrder is meaningful only if TabStop is True.

TabStop determines whether the user can tab to a control. If TabStop is True, the control is in the tab order.

Drag-and-drop properties

Two component properties affect drag-and-drop behavior:

DragMode determines how dragging starts. By default, DragMode is dmManual, and you must call the BeginDrag method to start dragging. When DragMode is dmAutomatic, dragging starts as soon as the mouse button goes down.

DragCursor determines the shape of the mouse pointer when the pointer is over a component that will accept an object being dragged.

"Dragging and dropping" shows how to use drag-and-drop in an application.


Text controls

Section topics	See also

Many applications require the user to enter text or need to present text to the user. The type of control used to hold the information depends on the size and format of the information.

Use this component:	When you want users to do this:
Edit	Edit a single line of text.
Memo	Edit multiple lines of text.
MaskEdit	Adhere to a particular format, such as a nine-digit postal code or a phone number. See the Library Reference or online Help for the special characters to use in constructing a mask.
RichEdit	Edit unlimited lines of text or use rich text format.
Properties common to all text controls

All of the text controls have these properties in common:

Text determines the text that appears within an edit box or memo control.

CharCase forces the case of the text being entered to lowercase, mixed case or uppercase.

ReadOnly specifies whether the user is restricted from making changes. By default all edit controls are editable.

MaxLength limits the number of characters in an edit box.

PasswordChar hides the text the user types by assigning a single character. PasswordChar is most often used to show asterisks instead of a user's password.

HideSelection specifies whether selected text will remain highlighted when the control does not have focus.

Properties shared by memo and rich text controls

In addition to the properties common to all text controls, memos and rich text controls have several other properties in common with each other:

Alignment specifies how text is aligned (left, right, or center) within the component.

Lines contains the text of a memo or rich edit component as a list of strings.

OEMConvert determines whether the text in the control is converted to OEM characters. If True, the text is converted. Otherwise, the characters remain as ANSI characters.

WordWrap determines whether the text will wrap at the right margin.

WantReturns determines whether return characters the user enters in the memo or rich edit components by pressing Enter affect the text in the control or go to the form. If WantReturns is True and the user presses Enter, a return character is entered in the memo or rich edit component. Otherwise, the return goes to the form.

Set WantTabs property to True to enable users to type tabs in a memo control.

Memo controls

A memo control handles text much like an edit-box component, but the memo component can handle multiple text lines.

The text in the memo is the value of the Text property. Your application can tell if the text changes by checking the Modified property.

To have the text in a memo automatically selected whenever it becomes the active control, set the AutoSelect property to True.

At runtime, you can select all the text in the memo with the SelectAll method.

To find out which text in the memo the user has selected, or to replace selected text, use the SelText property.

To select only part of the text or to find out what part of the text is selected, use the SelStart and SelLength properties.

Rich text controls

The rich text component is a memo control that supports rich text formatting. That is, you can change the formatting of individual characters, words, or paragraphs. It includes a Paragraphs property that contains information on paragraph formatting. Rich text controls also have printing and text-searching capabilities.

By default, the rich text editor supports

Font properties, such as typeface, size, color, bold, and italic format

Format properties, such as alignment, tabs, indents, and numbering

Automatic drag-and-drop of selected text

Display in both rich text and plain text formats. Set PlainText to True to remove formatting.


Specialized input controls

Section topics	See also

The following components provide additional ways of capturing input:

Use this component:	When you want users to do this:
ScrollBar	Select values on a continuous range
TrackBar	Select values on a continuous range (more visually effective than a scroll bar)
UpDown	Select a value from a spinner attached to an edit component
HotKey	Enter Ctrl/Shift/Alt keyboard sequences
Splitter	Resize controls that are aligned
Scroll bars

The scroll bar component is a Windows scroll bar, used to scroll the contents of a window, form, or control. In the OnScroll event handler, you write code that determines how the window, form, or control behaves in response to the user scrolling the scroll bar.

LargeChange determines how far a thumb tab moves when the user clicks the scroll bar on either side of the thumb tab.

SmallChange determines how far the thumb tab moves when the user clicks the arrows at the end of the scroll bar to scroll or uses the arrow keys on the keyboard. The default value is 1.

Min and Max property values together determine how many positions are available on the scroll bar for the thumb tab to move when the user scrolls the scroll bar.

Your application can set the position of the thumb tab with the Position property, or use the property to determine how far the scroll bar has scrolled.

Track bar controls

The track bar can set or adjust integer values on a continuous range, such as volume or brightness. A track bar consists of a bar that defines the extent or range of the adjustment, and an indicator that both shows the current value for the control and provides the means for changing the value.

The user moves the slide indicator by dragging to a particular location or clicking in the hot zone area of the bar, which moves the slide indicator directly to that location.

Use the Max and Min properties to set the upper and lower range of the track bar.

Use SelEnd and SelStart to highlight a selection range. See the following figure.

By default, a track bar has one row of ticks along the bottom. Set the TickMarks property to tmNone to remove them, or tmBoth to put ticks on both the top and bottom. See the following figure.

You can build either a horizontal or vertical track bar with the Orientation property.

TickStyle controls automatic or manual tickstyle.

Position sets a default position for the track bar, it also tracks the value the user has selected at runtime.

Three views of the track bar component

By default, users can move one tickmark up or down by pressing the up and down arrow keys. Set LineSize to change that increment.

Set PageSize to determine the number of ticks moved when the user presses Page Up and Page Down.

Up-down controls

Up-down controls are text boxes that accept a limited set of discrete ordered input values that make up a circular loop. An up-down control is a combination of a text box and a special control that incorporates a pair of buttons, it is similar to the spin button.

When the user clicks the text box or the buttons, the input focus is set to the text box of the control. The user can type a value directly into the control or use the buttons to increment or decrement the value.

Use Associate to attach the up-down control to a text control.

Use the AlignButton property to place the up-down component to the left or right of its associated text control.

To switch the orientation from vertical to horizontal, set Orientation to horizontal.

Use Max and Min to set limits on the values the up-down control can have.

By default, you can use the arrow keys to increment and decrement the up-down control. To disable keyboard interaction, set ArrowKeys to False.

The Position property holds the value as the control moves up or down. Use Position to set the default value at design time.

When the value of an up-down control is over 1000, it automatically places a separator between every three digits. To remove the separator, set Thousands to False.

By default, an up-down control stops at its maximum value. If you want the control to move back to the minimum value when it reaches the maximum, set Wrap to True.

Hot key controls

Use the hot key component to assign a hot-key sequence to transfer focus to any component. The HotKey property contains the current key combination of the hot-key control. Alt+A is the default. To modify the value of the HotKey property either edit it directly or set the value of the Modifiers property.

The Modifiers property allows selection of one or more modifier keys such as Ctrl, Alt , Extra, or Shift for the hot key. Modifier keys are used in combination with another non-modifier key such as characters, function keys, or arrow keys.

The InvalidKeys property allows the user to specify that one or more modifier keys should be considered invalid. If this property is set a default modifier should also be specified in the Modifiers property.

Splitter control

Add a splitter to a form between two aligned controls to allow users to resize the controls. The splitter sits between a control aligned to one of the edges of the form and the controls that fill up the rest of the client area. Give the splitter the same alignment as the control that is anchored to the edge of the form.

Use each control on the form as a separate pane. After each pane is placed, place a splitter with the same alignment to allow that pane to be resized. The last pane you place on the form should be client-aligned, so that it resizes automatically to fill up the remaining space after all other panes are resized.

Set MinSize to specify a minimum size the splitter must leave when resizing its neighboring control.

Set Beveled to True to give the splitter's edge a 3D beveled look.


Buttons and similar controls

Section topics	See also

Aside from menus, buttons provide the most common way to invoke a command in an application.  There are several variations of the standard text-based button:

Use this component:	To do this:
Button	Present command choices
BitBtn	Present command choices on buttons with glyphs
SpeedButton	Create tool bar buttons
CheckBox	Present Boolean options
RadioButton	Present a set of mutually exclusive choices
ToolBar	Arrange tool buttons and other controls in rows and automatically adjust their sizes and positions.
CoolBar	Display a collection of windowed controls within movable, resizable bands
Button controls

A button component is a push button control. Users choose button controls to initiate actions. Double-clicking a button at design time takes you to the button's OnClick event handler in the Code editor.

Set Cancel to true if you want the button to trigger its OnClick event when the user presses Esc.

Set Default to true if you want the Enter key to trigger the button's OnClick event. Enter triggers OnClick even if another control has focus.

Bitmap buttons

A bitmap button component is a push button control that can include a bitmap on its face. You can choose from predefined bitmap button styles or specify your own bitmap.

To choose a custom bitmap for your button, set the Glyph property.

Use Kind to automatically configure a button with a glyph and default behavior. The table in "Creating standard-command buttons" shows the complete list of standard bitmap buttons.

By default, the glyph is to the left of any text. To move it, use the Layout property.

The glyph and text are automatically centered in the button. To move their position, use the Margin property. Margin determines the number of pixels between the edge of the image and the edge of the button.

By default, the image and the text are separated by 4 pixels. Use Spacing to increase or decrease the distance.

Bitmap buttons can have 3 states: up, down, and held down. Set the NumGlyphs property to 3 to show a different bitmap for each state.

Speed buttons

Speed button components are buttons that usually have graphical images on their faces that execute commands or set modes. They have some unique capabilities that allow them to work as a set. Speed buttons are commonly used with panels to create tool bars.

By default, speed buttons appear in an up (unselected) state.  To initially display a speed button as selected, set the Down property to True.

If AllowAllUp is True, all of the speed buttons in a group can be unselected, or "up" state.  Set AllowAllUp to False if you want a group of buttons to act like a radio group.

To make speedbuttons act as a group, give the GroupIndex property of all the buttons the same non-zero value.

Adding speed buttons to tool bars is discussed in "Working with toolbars and cool bars."

Check boxes

A check box lets the user make a binary decision like yes/no, male/female.

Set Checked to True and a check mark appears in the check box, indicating the option is selected.

The value of the AllowGrayed property determines if a check box can have two or three possible states. If AllowGrayed is False, the default value, clicking a check box alternately checks and unchecks it. If AllowGrayed is True, clicking a check box either checks, grays, or unchecks it.

The State property determines the various states a check box control can have. cbUnchecked: the check box has no check mark indicating the user hasn't selected the option.  cbChecked: the check box has a check mark in it indicating the user has selected the option, cbGrayed: the check box is gray indicating a third state that is neither checked nor unchecked. Your application determines the meaning of a grayed check box.

RadioButton

Radio buttons present a set of mutually exclusive choices. You can use the radio button component to place individual buttons, or use the radio group component to set a rectangular region with a Caption property, and list your radio-button items using the Items property. See "Grouping components" for more information.

Use the Caption property to set a value for the radio button.

When Checked is True, a black circle appears in the radio button, indicating that the option is selected.

Tool bars

Tool bars provide an easy way to arrange and manage visual controls. You can create a tool bar out of a panel component and speed buttons, or you can use the ToolBar component, then choose New Button from its SpeedMenu for each button you want. Using the last way has these advantages:

All tool buttons on a tool bar maintain a uniform width and height.

Other controls you place on the tool bar are held in place by invisible tool buttons and maintain a uniform height.

Controls can automatically wrap around and start a new row when they do not fit horizontally on the tool bar.

Spaces and dividers (which are specially configured tool buttons) can group controls on the tool bar.

You have added display options, such as pop-up borders and transparency.

Adding a tool bar to a form is discussed in "Working with toolbars and cool bars."

Cool bars

A cool bar contains child controls that can be moved and resized independently. Each control resides on an individual band. The user positions the controls by dragging the sizing grip to the left of each band.

The cool bar requires version 4.70 or later of COMCTL32.DLL (usually located in the WINDOWS\SYSTEM or WINDOWS\SYSTEM32 directory) at both design time and runtime.

The Bands property holds a collection of TCoolBand objects. At design time, you can add, remove, or modify bands with the Bands editor. To open the Bands editor, select the Bands property in the Object Inspector, then double-click in the Value column to the right, or click the ellipsis (...) button. You can also create bands simply by adding new windowed controls from the palette.

You can specify if a user can reorder the bands by seting the FixedOrder property to True or False.

Specify if the bands automatically maintain a uniform height or width with the FixedSize property.


Handling lists

Section topics	See also

Lists present the user with a collection of items to select from.  Several components display lists:

Use this component:	To do this:
ListBox	Display a list of text strings. The user may select one or more.
ComboBox	Display a collapsible list of text strings. This component conserves form space.
TreeView	Display items hierarchically.
ListView	Display items graphically.
ImageList	Manage a list of images.
CheckListBox	Display a list with check boxes in front of each item
DateTimePicker	Display a list box for entering dates or times
Properties common to list controls

The following properties are common to the display of list controls:

Columns specifies the number of columns in a list control.

IntegralHeight specifies whether the list box shows only entries that fit completely in the vertical space.  If IntegralHeight is False, the bottom of the list box is at the location determined by its ItemHeight property, and the bottom item visible in the list might not be complete.

ItemHeight specifies the height of a list item in pixels.  The Style property can cause ItemHeight to be ignored.

The following properties are common to the control of lists:

Items uses a string list to fill the list control with values. See "Working with string lists" for information on how to use string lists.

ItemIndex indicates which item in the list is selected.

MultiSelect specifies whether a user can select more than one item at a time. The user can then use the Ctrl key to select multiple noncontiguous items, or the Shift key to select a range of items.

Sorted determines whether the list will be sorted in alphabetical order.

List boxes

A list box displays a list from which users can select one or more items. The following properties are key to using a list box.

You can add, delete, and insert items in the list box using the Add, Delete, and Insert methods of the Items property, which is a string list.

When MultiSelect is set to True, users can select more than one item. By default, the multiselect process for list boxes is to click on individual items with the Ctrl key pressed. Holding down the Shift key has no effect. To activate Shift to select a range of values, set both MultiSelect and ExtendedSelect to True.

The Style property determines how a list box displays its items. By default, Style is lbStandard, meaning that the list box displays each item as a string. By changing the value of Style, you can create owner-draw list boxes, meaning that items can be graphical and of either fixed or varying height.

For a discussion of implementing owner-draw controls in your application, see "Creating an owner-draw control."

Combo boxes

A combo box component is a control that combines an edit box with a list much like that of a list box. Users can either type text in the edit box or select an item from the list.

When users enter data into the combo box, either by typing text or selecting an item from the list, the value of the Text property changes.

By default, the drop-down list will display eight items. To increase or decrease displayed items, assign DropDownCount a new value.

Combo boxes have five different styles. Use the Style property to select the type of combo box you need:

Use csDropdown if you want a list of values like a list box but don't want to see the list of values until the user pushes the button. By default the text area will be editable.

Use csDropDownList style to make the combo box edit region read-only. The user can't edit an item or type in a new item, but can select items from the list.

csSimple to create a combo box with a fixed size list that does not close up.

Tree views

A tree view control is a special list box control that displays a set of objects as an indented outline based on their logical hierarchical relationship. The control includes buttons that allow the outline to be expanded and collapsed. You can use a tree view to display the relationship between a set of containers or other hierarchical elements.

You can optionally include icons with the text label of each item in the tree. Different icons can be displayed when the user expands or collapses the item in the tree. In addition, you can also include a graphic, such as a check box, that can be used to reflect state information about the item.

The control also supports drawing lines that define the hierarchical relationship of the items in the list and buttons for expanding and collapsing the outline.

Indent sets the number of pixels horizontally separating items from their parents.

Set ShowButtons to False to remove the '+' button that indicates the item can be expanded.

Set ShowLines to False to remove the connecting lines of the tree view.

Set ShowRoot to False to remove the lines connecting the root (top level) items.

DateTimePicker

DateTimePicker displays a list box for entering dates or times. To use DateTimePicker, you must have the latest version of COMCTL32.DLL (usually located in the WINDOWS\SYSTEM or WINDOWS\SYSTEM32 directory).


Grouping components

Section topics	See also

It is much easier for users to navigate through an application when the information is presented in groups.  Delphi provides several components that can be used to group information:

Use this component:	When you want this:
GroupBox	A standard group box with a title
RadioGroup	A simple group of radio buttons
Panel	A more visually flexible group of controls
ScrollBox	A scrollable region containing controls
TabControl	A set of mutually-exclusive notebook-style tabs
PageControl	A set of mutually-exclusive notebook-style tabs with corresponding pages, each of which may contain other controls
HeaderControl	To provide titles for columns of data
Group boxes

The group box component is a standard Windows group box. Use a group box component to group related controls on a form. The most commonly grouped controls in a group box are radio buttons.

Place the group box on the form, then select the components you want to appear in the group box from the Component palette, and place them in the group box.

The text that identifies the grouping appears as the value of the Caption property.

Radio groups

The radio group component simplifies the task of grouping radio buttons and getting them to work together. The radio group gives you a rectangular region with a Caption property.

Use the Items property of the radio group component to specify the choices in the box: click the ellipsis (...) button in the Value column on the Properties page of the Object Inspector to open the String List editor. Type the strings you want to appear next to the radio buttons, and press Enter.

You cannot select individual radio buttons in a radio group component. To respace the buttons, reize the radio group component. To edit their names, use the String List editor.

Panels

The panel component is used to place panels on a form on which other controls can be placed. For example, you can create a panel which is a tool bar. Panels are containers. Use a bevels component instead for purely visual effects.

Panels can be aligned with the form so that they maintain the same relative position to the form even when the form is resized.

The BorderWidth property determines the width in pixels of the border around a panel. The default value is 0, which means no border.

The Locked property determines whether a panel is replaced by an in-place active OLE object. If Locked is False, the OLE server can replace the panel. If Locked is True and the panel is aligned to one of the edges of the form, then the panel remains when an OLE object in an OLE container component is activated in place.  Use Locked to prevent status bars and the like from being replaced.

Page controls

The page control component is a page set that is used to make a multiple-page dialog box. To create a new page in a page control at design time, right-click the page control and choose New Page.

The ActivePage property specifies the active tab sheet object.

Set Multiline to True to allow multiple lines.

PageCount specifies the number of pages.

Pages gives you access to the pages of the tab sheet object.

TabHeight property sets the height in pixels of the tabs. TabWidth is the horizontal size in pixels of the individual tabs. If zero, TabWidth tabs will automatically size themselves to fit their text.

Tab controls

The tab control component has the appearance of notebook dividers.  To create a multiple page dialog box, use a page control. The tab control replaces the tab set included in Delphi 1.0.

Scroll boxes

Scroll box components make it possible to create scrolling areas on a form that are smaller than the entire form. The are many times when an application needs to display more information than will fit in a particular area. Some controls, such as list boxes and memos can automatically scroll their contents. But other controls, and sometimes even forms full of controls, need to be able to scroll. Delphi handles these scrolling regions with a control called a scroll box.

A scroll box is much like a panel or a group box, in that it can contain other controls, but a scroll box is normally invisible. However, if the controls contained in the scroll box cannot all fit in the visible area of the scroll box, it automatically displays one or two scroll bars, enabling users to move controls outside the visible region into position where they can be seen and used.

Set the boundaries of the scroll-box to the region you want to scroll.

Use the Kind property to determine whether a scroll box has horizontal or vertical scroll bars.

You can use the Align property of a scroll box to allow the scroll box to adjust its area to a form or a part of a form

Header controls

The header component is a sectioned visual control that displays text and allows each section to be resized with the mouse. At design time, resize a section by clicking the mouse button on a section border and dragging to the new size. At runtime, the user can resize the header by clicking and dragging with the left mouse button. The widths of the other sections that are not resized remain unchanged.

Use the Sections property to specify the separate sections in a header.

The control also supports the user dragging on the divisions that separate header parts to set the width of each column. As an option, you can support double-clicking on a division as a shortcut to a command that applies to formatting the column, such as automatically sizing the column to the largest value in that column.


Visual feedback

Section topics	See also

Well-designed applications provide users with information pertaining to the current state of the application.  This information is conveyed using the following components:

Use this component or property:	To do this:
Label	Display non-editable text strings
ProgressBar	Display the percentage of work remaining for a particular task
StatusBar	Display a non-editable status region at the bottom of a window
StaticText	Display non-editable text in a component that has a window handle
Hint and ShowHint	Activate fly-by of "tool-tip" help
HelpContext	Link in online help systems
Labels

The label component displays text on a form. Usually labels are placed adjacent to edit boxes or other controls. The label is a nonwindowed control, so it cannot receive focus.

Caption contains the text string for the label.

The FocusControl links the label control with another control on the form. If the caption of a label includes an accelerator key, the control specified in the FocusControl property becomes the focused control when the user uses the accelerator key.

If you want a label to appear on top of a graphic, but you want to be able to see through the label so that part of the graphic/bitmap isn't hidden, set the Transparent property to True.

The ShowAccelChar property determines how an ampersand (&) in the caption of a label appears. If ShowAccelChar is True, an ampersand appears as an underline under the character to its right in the caption indicating the underlined character is an accelerator character. Otherwise, the ampersand character appears as an ampersand.

Progress bars

A progress bar is a control you can use to show the percentage of completion of a lengthy operation. It consists of a rectangular bar that "fills" from left to right. Use the control as feedback for long operations or background processes.

Use Position to set a default position for the progress bar. At runtime, Position tracks the exact location as values increment.

Use Max and Min to set the range of Position.

By default, the progress meter will advance by a value of one. Assign StepBy a higher number to increase the increments. See the StepIt method in online Help for more information.


Status bars

Section topics	See also

A status bar usually occupies the bottom of a window and usually displays text. Although you can use Delphi's panel component to make a status bar, it is simpler to use the Windows status-bar component. You will generally subdivide the status bar into multiple text areas.

The status-bar component automatically has its Align property set to alBottom, which takes care of both positioning and size.

To create subpanels in the status bar component, use the Panels property: in the Values column of the Object Inspector, click the ellipsis (..) button to open the Panels editor.

Set each panel's Width property while the Panels editor is open and a panel is selected in the Panels editor. Set Alignment and other properties at this time, as well.

Add code to the individual status panels to reflect the status of the application.

Help or hint properties for a component

The following properties are common to all controls that show help and hints:

Hint is the text string that can appear when the user moves the mouse pointer over a control or menu item. To make the hint appear when the user moves the mouse over the component, set ShowHint to True.

ParentShowHint quickly shows all defined hints for controls in a group. Like the parent properties for font and color, ParentShowHint determines where a control looks to find out if hints should be shown.

HelpContext establishes a Help context number for the control.


Tabular display

Section topics	See also

Grids consist of a set of rows and columns that define a collection of cells.  What appears in each of the cells depends on the application and the type of grid control used:

Use this component:	To do this:
DrawGrid	Display an existing data structure in a grid
StringGrid	Display a grid of text strings
DBGrid	Display the contents of a dataset
Draw grids

A draw grid component is a grid control that permits the display of an existing data structure in column and row format.

The grid uses the OnDrawCell event to fill in the cells of the grid. If the DefaultDrawing property is False, the code you write in the OnDrawCell event handler draws in the cells. If DefaultDrawing is True, the contents of the cells are automatically drawn using some default values.

You can obtain the drawing area of a cell with the CellRect method. The MouseToCell method returns the column and row coordinates of the cell the mouse cursor is in.

You can determine which cell is selected in the grid by checking the value of the Selection property.

You can change the appearance and behavior of a data grid by changing the value of the Options property. For example, you can choose to allow the user to use the Tab key to move to a new column, you can decide to display grid lines between columns but not between rows, or let the user edit the data displayed in the grid.

Several properties affect the appearance of the grid. The DefaultColWidth and DefaultRowHeight properties determine the default widths and heights of the columns and rows.

You can change the width or height of a specific column or row with the ColWidths and RowHeights properties. You can choose to have fixed or nonscrolling columns and rows with the FixedCols and FixedRows properties, and you can assign the color of the fixed columns and rows with the FixedColor property. Set the width of the grid lines with the GridLineWidth property. Add scroll bars to the grid with the ScrollBars property.

You can determine which row is currently the top row in the grid, or set a specified row to be the top row with the TopRow property. To determine which column is the first visible column in the grid, use the LeftCol property. The values of the VisibleColCount and VisibleRowCount properties are the number of columns and rows visible in the grid.

In addition to these properties, methods, and events, this component also has the properties, methods, and events that apply to all windowed controls.

String grids

The string grid component is a grid control designed to simplify the handling of strings and associated objects while maintaining all the features of the draw grid component.

All the strings in a string grid are contained in the Cells property, which you can use to access a particular string within the grid. All the objects associated with the strings in a string grid are contained in the Objects property.

All the strings and their associated objects for a particular column can be accessed using the Cols property. The Rows property gives you access to all the strings and their associated objects for a particular row.

In addition to these properties, methods, and events, this component also has the properties, methods, and events that apply to all windowed controls.


Graphic display

Section topics	See also

Graphics, when used appropriately, can add that extra bit of flash and style which makes an application stand out.  To incorporate graphics into your Delphi applications use the following components:

Use this component	To do this:
Image	Display the contents of a graphics file
Shape	Display a geometric shape
Bevel	Display 3D lines and frames
PaintBox	Programmatically display graphics
Animate	Silently display an AVI file
Images

The image component displays a graphical image, like a bitmap, icon, or metafile on a form. Delphi includes a library of images to choose from in the IMAGES subdirectories. Use the following properties to set up the image component.

Select an image to display by clicking on the Picture property.

By default, any image is centered in the Image component, to align the image to the upper left corner, change Center to False.

To make the image shrink or grow with the border, change Stretch to True.

Shapes

The shape component displays a geometric shape on the form. It is a nonwindowed component and cannot receive user input.

You determine which geometric shape the shape control assumes by setting the Shape property.

To change the color of the border, use BorderColor.

To change the color of the shape or to add a pattern, use the Brush property to fill it. How the shape is painted depends on the two nested properties of the Brush object, Color and Style.

Bevels

The bevel component lets you put beveled lines, boxes, or frames on the forms in your application.

A panel component has two bevels, an outer bevel drawn next to the border of the control, and an inner bevel drawn inside the outer bevel. Use BevelInner and BevelOuter to determine the style of the bevel:  none, raised, or lowered.

The BevelWidth property determines the width in pixels between the inner and the outer bevels of a panel.

Paint boxes

The paint box component provides a way for your application to draw on the form using code in a specified rectangular area. It prevents drawing outside the boundaries of the paint box. Once a paint box is added to your form, your application can use the OnPaint event handler to draw on the paint box's Canvas.

For information on how to draw on a canvas, see "Drawing graphics at runtime."

Animation control

The animation component is a window that silently displays an Audio Video Interleaved (AVI) clip. An AVI clip is a series of bitmap frames, like a movie. Although AVI clips can have sound, animation controls work only with silent AVI clips. The files you use must be either uncompressed AVI files or AVI clips compresed using run-length encoding (RLE). These are some of the properties of an animation component:

ResHandle is the Windows handle for the module that contains the AVI clip as a resource. Set ResHandle at runtime to the instance handle or module handle of the module that includes the animation resource. After setting ResHandle, set the ResID or ResName property to specify which resource in the indicated module is the AVI clip that should be displayed by the animation control.

Set AutoSize to True to have the animation control adjust its size to the size of the frames in the AVI clip.

StartFrame and StopFrame specify in which frames to start and stop the clip.

You can set CommonAVI to display one of the common Windows AVI clips provided in Shell32.DLL.

Specify when to start and interrupt the animation by setting the Active property to True and False, respectively, and how many repetitions to play by setting the Repetitions property.

The Timers property lets you display the frames using a timer. This is useful for synchronizing the animation sequence with other actions, such as playing a sound track.


Windows common dialog boxes

Section topics	See also

The dialog box components on the Dialogs page of the Component palette make the Windows "common"dialog boxes available to your Delphi applications. These dialog boxes provide all Windows-based applications with a familiar, consistent interface that enables the user to perform common file operations such as opening, saving, and printing files.

Each dialog box opens when its Execute method is called. Execute returns a Boolean value: if the user chooses OK to accept any changes made in the dialog box, execute returns True; if the user chooses Cancel to escape from the dialog box without making or saving changes, Execute returns False.


Customizing the Component palette

Section topics	See also

You have control over what components appear on the Component palette and in what order. You can:

Hide, rearrange, and rename components

Install additional components

Create component templates and add them to the palette


Rearranging the Component palette

Section topics	See also

To add, delete, or rearrange Component palette pages or to rearrange components on the pages:

1.	Choose Component|Configure Palette. Or, choose Tools|Environment Options and select the Palette page.

The Palette page lists each page on the palette and the components that appear on that page.

2.	You can do the following:

To move or rename a page, select the page and click the appropriate button in the dialog box: Delete, Rename, Move Up, or Move Down. (Before deleting a page, start by hiding each component on the page. Then select the page itself in the Pages list box and click Delete.)

To add a page, click Add.

To rearrange components on a page, select the page. Then select the component you want to move, and click the appropriate button in the dialog box: Hide, Move Up, or Move Down.

To move components from one page to another, select the page whose component you want to move. Select the component from the Components list and drag it to another page in the Pages list.

To show a component you've previously hidden, select the [All] page in the Pages list. Then scroll down the Components list toward the bottom to find the component. Drag it to the page you want.

3.	Choose OK.


Adding components to Delphi

Section topics	See also

In Delphi 2.0, the way to add components to the IDE was to build them into the VCL and to develop customized library files. In Delphi 3.0, you use design-time packages instead, and you don't recompile the VCL.

You can install your own component packages or component packages from third-party developers. Then, in the Project|Options dialog box you specify all the packages that you want your project to use.

The DCLUSR30 design-time package is provided as a default container for new components.

Note:	Writing your own components generally requires a more in-depth knowledge of Object Pascal and object-oriented programming than using pre-built components. The process is explained in the Developer's Guide.

Installing component packages

Components you're installing to the Delphi IDE must be contained in a package. (See "Creating and editing packages.") The unit file that contains your component source code must follow the model for component source files described in the Delphi Developer's Guide.

To install or uninstall your own components, or components from a third-party vendor:

1.	Copy or move the necessary files to a local directory.

If the package is shipped with separate .DPL, .DCP, and .DCU files, be sure to copy all of them. (For information about .DCP and .DCU files, see "Package files created by a successful compilation.")

If the package is shipped as a .DPC (package collection) file, only the one file need be copied; the .DPC file contains the other files. (For more information about package collection files, see "Package collection files.")

The directory where you store the .DCP file--and the .DCU files, if they are included with the distribution--must be in the Delphi Library Path. It is customary to put executable files in the Delphi\BIN directory.

2.	Choose Component|Install Packages. Or, choose Project|Options and click the Packages tab.

3.	A list of available packages appears under Design Packages.

To add a package to the list, click Add and browse in the Open Package dialog box for the directory where the .DPL or .DPC file resides (see step 1). Select the .DPL or .DPC file and click Open. If you select a .DPC file, a new dialog box appears to handle the extraction of the .DPL and other files from the package collection.

To install a package, select the check box next to it.

To uninstall a package, uncheck its check box.

To see a list of components included in an installed package, select the package and click Components.

To remove a package from the list, select the package and click Remove.

4.	Click OK.

The components in the package are installed on the Component palette pages specified in the components' RegisterComponents procedure, with the names they were assigned in the same procedure.

New projects are created with all available packages installed, unless you change the default settings. To make the current installation choices into the automatic default for new projects, check the Default check box at the bottom of the dialog box.

Adding ActiveX controls

You can add ActiveX controls to the Delphi component library, so long as they conform to Microsoft specifications. To do so, you specify the .OCX file that contains the control you want to add. Delphi then automatically creates a "wrapper" unit (a .PAS file) for the control to make it into a recognizable object type, and adds it to the list of installed units. You can specify a name for the control and which palette page you'd like it to appear on.

To add an ActiveX control to the component library:

1.	Choose Component|Import ActiveX Library.

2.	In the dialog box, choose the Help button for further instructions.


Creating component templates

Section topics	See also

You can create component templates that are made up of a number of components. After arranging components on a form, setting their properties, and writing code for them, you can save them as a component template. Later, by selecting the template from the Component palette, you can place the preconfigured components on a form in a single step; all associated properties and event-handling code are added to your project at the same time.

Once you place the template on a form, you can reposition the components independently, reset their properties, and create or modify event handlers for them just as if you had placed each component in a separate operation.

To create a component template:

1.	Place and arrange components on a form as desired.

2.	Select the components. The easiest way to select several components is to drag the mouse over all of them. Gray handles appear at the corners of each selected component.

3.	Choose Component|Create Component Template.

4.	Specify a name for the component template in the Component Name edit box. The default proposal is the component type of the first component selected in step 2 followed by the word "Template". For example, if you select a label and then an edit box, the proposed name will be "TLabelTemplate". You can change this name as desired. Be careful not to duplicate existing component names.

5.	In the Palette Page edit box, specify the Component palette page where you want the template to reside. If you specify a page that does not exist, a new page is created when you save the template.

6.	Specify a bitmap to represent the template on the palette. The default proposal will be the bitmap used by the component type of the first component selected in step 2. To browse for other bitmaps, click Change. The bitmap you choose must be no larger than 24 pixels by 24 pixels.

7.	Click OK.

To remove a component template from the Component palette,
Choose Component|Configure Palette. (See "Rearranging the Component palette.")


See also

About the Component palette

Form files

Adding a form to a project

Programming with Delphi objects

Inheriting data and code from an object

Form properties that show in controls

Setting the tab order

Dragging and dropping

Working with text

Different kinds of toolbar control

Adding a cool bar component

Working with string lists

Creating an owner-draw control

Working with grids

Drawing graphics at runtime

Using Windows common dialogs

Developing custom dialog boxes

Creating ActiveX controls


Creating a user interface

Section topics	See also

A Delphi application user interface usually contains multiple forms: a main form, which is the primary user interface, and other forms such as dialog boxes, secondary windows, and so on.

You develop your application by customizing the main form and adding and customizing forms for other parts of the interface. You customize forms by adding components, setting their properties, and creating menus to provide user control over the application at runtime.

You can begin your form design from one of the objects in Delphi's Object Repository, and you can add any form you design to the Object Repository so you can reuse it in other projects. Likewise, you can use menu templates as starting points for menus you customize to suit your programming needs.

These topics show how to:

Add a form to a project

Manipulate components in forms

Set component properties

Develop custom dialog boxes

Set runtime auto-create order


Adding a form to a project

Section topics	See also

When you start Delphi for the first time, it opens with a new project consisting of a single, blank form. You can use that as a starting point for your project, or you can delete the form and its unit and choose a wizard to create a form for you. Or, you can choose another form, if you have one available. (See information about the Object Repository in the section "Sharing objects.")

To use a wizard or add a different form to the project, choose File|New. Delphi opens the New Items dialog box, as shown:

You have these options:

Use a form wizard (from the Forms page) to create a form to your specifications and add it to your project.

If you've defined a default new form, and you want to add it to your project, choose the Form icon from the New page. (The default new form is defined in the Object Repository dialog box. You can change the default form at any time using that dialog box.)

If you have other forms available to you in the Object Repository, you can choose one of those from the Forms page and specify how you want to use it. ("Sharing objects" explains the Copy, Inherit, and Use options. Or, just click the Help button in the dialog box for information.)

The Object Repository contains wizards, a standard form, and any forms, dialog boxes, data modules, and projects you have added. All of them appear listed in the New Items dialog box. The dialog box also includes a page for your current project and lists the forms you already have in the project. You can choose one of those forms, as well, giving you the ability to inherit from a form in your project. This way, if you create an application that has several essentially similar forms with only slight variations, you can create one version of the form, then create the others by inheriting. Changes you make in the standard form are reflected in all the inherited forms.

Delphi adds the form you choose and its associated unit to the project you have open. You can now use this form the way you would any form in a project. You can customize it, add components and code to it, and so on.

Modifications you make might affect the original item in the Object Repository, depending on the sharing option you chose.

Note:	With no project open, it is still possible to choose File|New and select a form from the Object Repository. The form's unit file then opens as a reference file . If you subsequently open a project or create a new project, the open template form is not part of that project. To save it as part of the project, you must explicitly add it to the project.


Specifying a form as the project main form

Section topics	See also

The first form you create and save in a project becomes, by default, the project's main form, which is the first form created at runtime. (For more information about form creation order, see "Controlling the form auto-create order.") As you add forms to your projects, you might decide to designate a different form as your application's main form. Also, specifying a form as the main form is an easy way to test it at runtime, because unless you change the form creation order, the main form is the first form displayed in the running application.

To change the project main form:

1.	Choose Project|Options and select the Forms page.

2.	In the Main Form combo box, select the form you want as the project main form and choose OK.

Now if you run the application, your new main form choice is displayed.


Adding additional forms

Section topics	See also

Add additional forms to your project the same way you added the first form. You can see all your project's forms and their associated units listed in the Project Manager (View|Project Manager).


Linking forms

Section topics	See also

Adding a form to a project adds a reference to it in the project file, but not to any other units in the project. Before you can write code that references the new form, you need to add a reference to it in the referencing forms' unit files. This is called form linking.

A common reason to link forms is to provide access to the components in that form. For example, you'll often use form linking to enable a form that contains data-aware components to connect to the data-access components in a data module.

To link a form to another form:

1.	Select the form that needs to refer to another.

2.	Choose File|Use Unit.

3.	Select the name of the form unit for the form to be referenced.

4.	Choose OK.

Linking a form to another just means that the uses clauses of one form unit contains a reference to the other's form unit, meaning that the linked form and its components are now in scope for the linking form. For more explanation of scope, units, and uses clauses, see "Writing Object Pascal code."

Avoiding circular unit references

When two forms must reference each other, it's possible to cause a "Circular reference" error when you compile your program. To avoid such an error, do one of the following:

Place both uses clauses, with the unit identifiers, in the implementation parts of the respective unit files. (This is what the File|Use Unit command does.)

Place one uses clause in an interface part and the other in an implementation part. (You rarely need to place another form's unit identifier in this unit's interface part.)

Do not place both uses clauses in the interface parts of their respective unit files. This will generate the "Circular reference" error at compile time.


Customizing a form component

Section topics	See also

In addition to the properties common to all components, such as Height, Width, and so on, forms have a number of unique properties you can use to control their appearance and behavior. The properties common to all components and the ways you can set and read properties are described in "Understanding components." This section introduces those items unique to forms.


Controlling form appearance

Section topics	See also

A number of properties determine the way a form appears when shown at runtime. You can get information about all these properties in online Help, either by selecting the property in the Object Inspector and pressing F1, or by searching the Help index for the name of the property.

The following figure shows a simple Delphi form with the meanings of several of its display properties indicated. Note that you'll set most of these at design time by, for example, resizing the form on the screen. But you can also set the properties directly through the Object Inspector.

Some properties, such as BorderStyle, affect both the appearance and behavior of the window at runtime. For example, setting BorderStyle to bsNone produces a window with no visible border, but also makes the window nonresizable, since there is no border to grab and drag.


Controlling form behavior

Section topics	See also

Many form properties change the way a window behaves, or reflect its state, without changing its appearance. The following is a brief list of some of the common ones:

ActiveControl sets which control in the form gets focus when the form is shown. When the window is active, ActiveControl reflects which control actually has focus.

Position, WindowState, and Scaled determine the way the window appears on the screen. Position controls whether the window takes the exact position and size given it at design time, or whether it uses the size and position suggested by Windows at runtime. WindowState sets the initial state of the run-time window: minimized, maximized, or normal. Scaled determines whether the form will adjust its size at runtime based on the resolution of the display screen.

ShowHint determines whether the form displays a "tool-tip" hint when the user rests the mouse over the form momentarily. The text displayed is set in the Hint property.

FormStyle controls multiple document interface (MDI) behavior. Delphi forms can serve as MDI frames or MDI children as well as normal windows. "Creating an MDI application" shows how to build an MDI application.

KeyPreview enables the form to "trap" keystrokes before they go to the active control, allowing a form to filter keystrokes or redirect them.

PrintScale determines whether the form's Print method prints the form's image proportionally or fits it to fill the printed page.


Form properties that show in controls

Section topics	See also

Many of the properties you set on a form also affect the settings of similar properties in the controls you place in that form. These properties enable you to set attributes such as font and color for all the controls in a window without having to set each one individually.

These properties that can "show through" in controls are Color, Ctrl3D, Font, and ShowHint. Visual components that you place in a form have their own Color, Ctrl3D, Font, and ShowHint properties, but they also have corresponding properties called ParentColor, ParentCtrl3D, ParentFont, and ParentShowHint. (The parent component is the one that visually contains the control. It's often the form, but might be a group box or panel component.) If any of those "parent" properties are set to True, the control's property assumes the value of the parent's property. If you change one of the corresponding properties in the control, it turns off the parent property and uses the value you set.


Manipulating components in forms

Section topics	See also

If you're comfortable using controls in a graphical user interface (GUI) environment, much of the material discussed in this section might be familiar to you. This section shows you how to:

Add components to the form

Select components in the form

Group components

Cut, copy, and paste components

Delete and restore components

Align components

Note:	Keyboard support is available for many of the tasks documented here. For more information about keyboard techniques, search the online Help index for "keyboard shortcuts" and choose "Form keyboard shortcuts" from the Topics Found list.


Adding components to the form

Section topics	See also

When you add components to the form, you can either accept the default component size, or resize the component as you add it. You can also easily add multiple copies of a component.

To add a component to the center of the form:
Simply double-click the component in the Component palette Take care when adding components in this manner. New components can completely cover components that are already on the form. Clicking too many times may result in more components on your form than you intended, with the extra components hidden beneath the ones placed most recently.

To add a component to a specific location in the form:

1.	Click the component on the Component palette.

2.	Move the cursor to where you want the upper left corner of the component to appear in the form, then click the form.

The component appears in its default size, in the position you clicked on the form.

When you add a component to a form, Delphi generates an instance variable, or field, for the component and adds it to the form's type declaration:

type

 TAboutBox = class(TForm)

   BitBtn1: TBitBtn;	{ this is the code that Delphi added )

 end;	{ some additional code omitted for brevity }

Similarly, when you delete a component, Delphi deletes the corresponding type declaration. For more information, see "Deleting event handlers."

Adding multiple copies of a component

To add multiple copies of the same component:

1.	Press and hold the Shift key.

2.	Click the component in the palette, then click in the form once for each copy you want of the component. (You don't need to hold down the Shift key after the component is selected.)

Click the pointer icon to clear the selected component.

Sizing a component as you add it

You can resize and move components after they're on the form, but you can also size them as you place them.

To size a component as you add it:

1.	Select the component on the Component palette.

2.	Place the cursor where you want the component to appear in the form, then drag the mouse pointer before releasing the mouse button.

3.	As you drag, an outline appears to indicate the size and position of the control.

Release the mouse button when the outline appears as you want it.


Selecting components in the form

Section topics	See also

There are several ways to select components in the form; most, but not all, of these methods also apply to components contained by other components, such as the Panel or GroupBox component.

To select a single component, do one of the following:

Click the component in the form.

Choose from the drop-down list located at the top of the Object Inspector.

With focus in the form, tab to the component.

You can select multiple components. Multiple selection applies to all components in the form, including container components and any components they contain; but when the form itself is selected, no other components can be selected along with it.

To select multiple components, do one of the following:

First hold down the Shift key, and then click the components, one at a time.

Click the form outside one of the components and drag over the other components you want to select. (If the components are inside a Panel or GroupBox component, press Ctrl, and then drag.)

As you drag, you surround the components with a dotted rectangle. When this rectangle encloses all the components you want to select, release the mouse button. In some cases, you might need to rearrange the components before they can be easily enclosed within a rectangle.

To select all components in a form,
Choose Edit|Select All.

Resizing selected components

When a component is selected on a form, small squares called sizing handles appear on the perimeter of the component. You can use these handles to resize one or more components.

To resize a single component:

1.	Select the component on the form.

2.	Drag a sizing handle until you are satisfied with the component's size.

When you release the mouse button, the component is redrawn in the new size.

To resize multiple components:

1.	Select the components you want to resize as a group.

2.	Choose Edit|Size to display the Size dialog box.

3.	Select appropriate sizing options, then choose OK.


Grouping components

Section topics	See also

Besides the form itself, Delphi provides several components—such as the group box, panel, page control, and scroll box--that can contain other components. These are often referred to as container components. You can use these container components to group other components so that they behave as a unit at design time. For instance, you might group components, such as speed buttons and check boxes, that provide related options to the user. You often use container components such as the panel component to create customized tool bars, backdrops, status bars, and so on.

For information about creating tool bars, see "Working with toolbars and cool bars." or search the online Help index for "tool bars."

When you place components within container components, you create a new parent-child relationship between the container and the components it contains. Design-time operations you perform on these "container" (or parent) components, such as moving, copying, or deleting, also affect any components grouped within them.

Note:	The form remains the owner for all components, regardless of whether they are parented within another component. For more information about the difference between a component's parent and owner, search the online Help index for "Owner property" and "Parent property."

You generally want to add container components to the form before you add the components you intend to group, because it's easiest to add components that you want grouped directly from the Component palette into the container component.

Once a component is in the form, you can add it to a container component by cutting and then pasting it.

To group components:

1.	Add a group box or panel component to the form.

2.	While the container component is selected, add components as you normally would.

As you add components, they appear inside the container component.

When you double-click a component in the palette, it appears in the middle of whichever eligible receiving component has focus--either the form, or a container component in the form. If you select and then place components, the container component needn't be selected, so long as you click within it when you place the component.

To add multiple copies of a component to a container:

1.	Press Shift and then select a component from the palette.

2.	Click anywhere in the container component.

Each subsequent click continues to place the component in whatever eligible receiving component (including the form) is clicked.

Select the pointer icon when you have finished adding components.


Cutting, copying, and pasting components

Section topics	See also

You can cut, copy, and paste components between forms, or between a form and a container component such as a panel or group box. The container component can be on the original form or in another form.

Note:	When you copy components, you copy their property settings and event-handler associations as well. For example, copying a button with a Caption property of Button1 and an event handler called TForm1.Button1Click creates another button with the same Caption property and an OnClick event handler that calls the code in TForm1.Button1Click. However, Delphi renames the copied button so that each button maintains a unique identifier.

Three general rules apply to all cut, copy, and paste operations:

You can select multiple components to be cut or copied, but the selection can include only those components within a single parent. That parent can be either a form or a container component within the form, but not both.

If you individually select components that have different parents and then attempt to cut or copy them, only the components in the parent of the component you first selected are cut or copied. For example, if you first select a button in the form, then select a check box in a panel on the form, and then attempt to cut or copy the two selected components, only the button is cut or copied.

When you paste one or more components, they appear in whichever eligible receiving component has focus.

When you cut, copy, or paste a component, any associated event handler code is not duplicated, but the copied component retains associations to the original event handlers.

To cut or copy a component or components to the Clipboard:

1.	Select the component(s).

2.	Choose Edit|Cut to remove the component(s) from their current location and place them on the Clipboard. Or, choose Edit|Copy to make a copy of the component(s) and place the copy on the Clipboard.

To paste a component or components that you have cut or copied to the Clipboard:

1.	Select the form, or other container component where you want the pasted components to appear.

2.	Choose Edit|Paste.


Deleting and restoring components

Section topics	See also

To delete a component:

1.	Select the component in the form.

2.	Press Del, or choose Edit|Delete.

To restore a component that's just been deleted,
Choose Edit|Undelete.

This restores the component just as it was, including any customizations you might have made to it, such as setting properties or writing code.

Caution:	You must choose Undelete before performing any other operation, or the components are not restored.


Aligning components

Section topics	See also

You can align components relative to each other, or relative to the form. Once you select the component(s) you want to align, you can use the Alignment palette or the Alignment dialog box to set the alignment. When aligning a group of components, the first component you select is used as a guide to which the other components are aligned.

To align components by using the Alignment palette:

1.	Select the components.

2.	Choose View|Alignment Palette.

The Alignment palette appears.

3.	Select an alignment icon from the palette.

Alignment palette

To align components using the Alignment dialog box:

1.	Select the components.

2.	Choose Edit|Align.

The Alignment dialog box appears.

3.	Select the alignment options you want in the dialog box.

4.	Choose OK to put your alignment options into effect.

You can continue to choose or modify alignment options as long as the components remain selected.

For more information about the Alignment palette and the Alignment dialog box, see online Help.

Using the form grid as an alignment guide

The evenly spaced dots that appear in the form at design time are the form grid. The grid makes it easier to align components visually:

By default, both the grid and its Snap To Grid option, which causes the left and top sides of each component to always align with the nearest grid markings, are enabled. You can, however, disable the Snap To Grid option, or disable the grid altogether. You can also modify how far apart the grid lines appear.

To set form grid options:

1.	Choose Tools|Environment Options to display the Environment Options dialog box.

2.	On the Preferences page, make your changes.

Locking the position of components

Once you have aligned the components on a form, you can prevent components from being moved accidentally.

To lock the position of the components on a form,
Choose Edit|Lock Controls from the Delphi menu bar.


Setting component properties

Section topics	See also

This section discusses different ways you can set component properties at design time. You can also change property values while an application is running. This is discussed in the section, "Setting properties at runtime."

For more information about the properties for each component, search online Help for the keyword components, or see the topic Visual Component Library Components, and select the component whose properties you want to view. You can also press F1 with the component selected in the form.

Note:	The components on the ActiveX and Samples pages of the Component palette are provided as examples only, not formally part of the Delphi VCL, and therefore are not documented as part of Delphi.


How the Object Inspector displays properties

Section topics	See also

The Object Inspector dynamically changes the set of properties it displays, based on the component selected. The Object Inspector has several other behaviors that make it easier to set component properties at design time.

When you use the Object Inspector to select a property, the property remains selected in the Object Inspector while you add or switch focus to other components in the form, provided that those components also share the same property. This enables you to type a new value for the property without always having to reselect the property.

If a component does not share the selected property, Delphi selects its Caption property. If the component does not have a Caption property, Delphi selects its Name property.

When more than one component is selected in the form, the Object Inspector displays all properties that are shared among the selected components. This is true even when the value for the shared property differs among the selected components. In this case, the property value displayed is either the default or the value of the first component selected. When you change any of the shared properties in the Object Inspector, the property value changes to the new value in all the selected components.

There is one notable exception to this: when you select multiple components in a form, the Name property no longer appears in the Object Inspector, even though all components have a Name property. This is because you cannot assign the same value for the Name property to more than one component in a form.


Tab-jumping to property names in the Object Inspector

Section topics	See also

Pressing Tab acts as a toggle between the Value column and the Property column of the Object Inspector. Whenever you are in the Value column (as you are by default when you select a property with the mouse), pressing Tab moves you to the Property column.

To navigate quickly to a property in the Object Inspector:

1.	Activate the Property column (just press Tab if your cursor is in the value column).

2.	Type the first letter of the property you want.

The cursor jumps to the first property beginning with that letter.

3.	Type the second letter of the property, and so on, until the cursor is at the property you want.

Now, pressing Tab places the cursor in the Value column, where you can begin entering your edits.


Displaying and setting shared properties

Section topics	See also

You can set shared properties to the same value without having to individually set them for each component.

To display and edit shared properties:

1.	In the form, select the components whose shared property you want to set.

The Properties page of the Object Inspector displays only those properties that the selected components have in common. (Notice, however, that the Name property is no longer visible.)

2.	With the components still selected, use the Object Inspector to set the property.

For information about more ways to set properties at design time, search the online Help index for "setting properties."


Using property editors

Section topics	See also

The Object Inspector provides several ways to edit properties at design time. So far, these topics have described two such ways: directly typing in values, or choosing from a drop-down list of pre-existing values.

In addition, with certain properties, double-clicking in the Value column opens a dialog box that you use to set the property. Likewise, with some components, double-clicking the component in the form opens such a dialog box. Properties you can edit through a dialog box are indicated in the Object Inspector by an ellipsis (...) in the Value column beside that property.

Example of a property editor

The Image component provides an example of a dialog box property editor. When you double-click an Image component in a form, the Picture Editor dialog box appears, as shown:

Use the dialog box to specify the image you want displayed in the component.

For detailed information about other types of property editors, see the online Help topic "property editors."


Setting properties at runtime

Section topics	See also

Any property you can set at design time can also be set at runtime by using code. There are also properties that can be accessed only at runtime. These are known as runtime-only properties.

To set a component property at design time by using the Object Inspector:

1.	Select the component.

2.	Specify the property (by selecting it from the Properties page).

3.	Enter a new value for that property.

Setting properties at runtime involves the same steps: in your source code, you specify the component, the property, and the new value, in that order. Run-time property settings override any settings made at design time.

Using properties to customize forms at runtime

When you include a form, such as a dialog box, in several projects, you should avoid making design-time changes to the form that might conflict with the use of the form in another project. Instead, write code that changes the form's properties at runtime.

For example, the caption of an About box generally includes the name of the program that uses it. If you use the same About box in several projects simultaneously, you don't want to accidentally use the name from another application.


Developing custom dialog boxes

Section topics	See also

One of the most common ways that Windows-based applications interact with users is through dialog boxes. Dialog boxes supply information about the application, and in most cases accept input from the user. The About box is an example of the simplest kind of dialog box. It doesn't accept any input from the user (other than a button click), but it does provide useful information. A message box that displays a warning or error message is also a kind of dialog box.

In addition to routines that display simple, predesigned dialog boxes, and Form Template dialog boxes such as the Password Dialog Template, Delphi provides a number of fully designed dialog boxes as components. These appear on the Dialogs page of the Component palette, and include components that encapsulate the Windows common dialog boxes. You can specify different options for these dialog boxes, such as whether a Help button appears in the dialog box, but for the most part you cannot change their overall design.

It's useful, therefore, to know how to build a dialog box from scratch, since you'll undoubtedly want to design your own. This section discusses the most common considerations when designing any dialog box, including:

Making a dialog box modal or modeless

Setting form properties for a dialog box

Providing command buttons

Creating standard-command buttons

Setting the tab order

Testing the tab order

Removing a component from the tab order

Enabling and disabling components

Setting the focus in a dialog box


Making a dialog box modal or modeless

Section topics	See also

At design time, dialog boxes are simply customized forms. At runtime they can be either modal or modeless. When a form runs modally, the user must explicitly close it before working in another running form. Most dialog boxes are modal.

When a running form is modeless, it can remain onscreen while the user works in another form (for example, the application main form). You might create a modeless form to display status information, such as the number of records searched during a query, or information the user might want to refer to while working.

Note:	If you want a modeless dialog box to remain on top of other open windows at runtime, set its FormStyle property to fsStayOnTop.

Forms have two methods that govern their run-time modality. To display a form in a modeless state, you call its Show method; to display a form modally, you call its ShowModal method.

The following two procedures use the Delphi About box form template to demonstrate the Show and ShowModal methods of the form.


Setting form properties for a dialog box

Section topics	See also

By default, Delphi forms have Maximize and Minimize buttons, a resizable border, and a Control menu that provides additional commands to resize the form. While these features are useful at runtime for modeless forms, modal dialog boxes seldom need them.

Delphi provides a BorderStyle property for the form that includes several useful values. Setting the form's BorderStyle to bsDialog implements the most common settings for a modal dialog box:

Removing the Minimize and Maximize buttons

Providing a Control menu with only the Move and Close options

Making the form border non-resizable, and giving it a "beveled" appearance

This table shows other form property settings that can be used, individually or in concert, to create different form styles:

Property	Setting	Effect
BorderIcons
biSystemMenu	False	Removes Control (System) menu
biMinimize	False	Removes Minimize button
biMazimize	False	Removes Maximize button
BorderStyle
	bsSizeable	Enables the user to resize the form border
	bsSingle	Provides a single outline, non-resizable border
	bsNone	No distinguishable border; not resizable
	bsDialog	Window has a border, but not resizable
	bsToolWindow	Makes the title bar small; window is not resizable
	bsSizeToolWindow	Makes the title bar small; window is resizable
Note:	Changing these settings doesn't change the design-time appearance of the form; these property settings become visible at runtime.


Providing command buttons

Section topics	See also

Depending on whether you intend to use your dialog box in a modal or modeless state, you'll want to provide certain command buttons in the dialog box. For modal dialog boxes, you need to provide the user with a way to exit the dialog box. It's fairly standard design to provide one or more command buttons for this purpose. For simple modal dialog boxes, such as a message box, one button is often sufficient. Such a button might be labeled, for instance, "OK" or "Close." (If you have another button in the dialog box labeled "No," as described in the next paragraph, this button might be labeled "Yes.")

In cases where the dialog box accepts input from the user, you want to provide users with a choice of whether or not to process their input upon exiting the dialog box. You can do this by means of an additional button labeled, for example, "Cancel" or "No." (If your dialog box explicitly asks a question of the user, you might want to label this button "No"; otherwise, "Cancel" is usually more appropriate.)

Your code controls what happens when a user chooses a command button, for example, whether changes are processed or not.

By setting properties of the Button component, you can call a button's event handler code when the user presses Enter or Esc; and you can specify that the dialog box closes when the user chooses a command button, without writing any additional code.

Executing button code on Esc

Delphi provides a Cancel property for Button components. When your form contains a button whose Cancel property is set to True, pressing the Esc key at runtime executes any code contained in the button's OnClick event handler.

To designate a button as the Cancel button,
Set its Cancel property to True.

Executing button code on Enter

Buttons have a Default property that operates similarly to the Cancel property. When your form contains a button whose Default property is set to True, pressing Enter at runtime executes any code contained in the button's OnClick event handler--unless another button has focus when the Enter key is pressed.

Even if your form contains a default button, another button can take focus away at runtime. Pressing the Enter key calls the OnClick event handler code of the button with focus, overriding any other button's Default property setting. (The button with focus is indicated by a darker, thicker border than that of other buttons in the dialog box.)

Note:	Although other components in a form can have focus, only button components respond when the user presses Enter. The default button takes the OnClick event when another non-button component in the form has focus. For an example of this, see "Setting focus at runtime."

To specify a button as the default button,
Set its Default property to True.

To change focus at runtime,
 Call the button's SetFocus method.

For more information, see "Setting the focus in a dialog box."

Closing a dialog box when the user chooses a button

You can specify that a modal dialog box close automatically when the user chooses any button whose ModalResult property has a nonzero value. By setting ModalResult to match a button's caption, you can also determine which button the user chose. For example, if you have a Cancel button, set its ModalResult property to mrCancel; if your form contains an OK button, set its ModalResult to mrOK. Both buttons will close the form when chosen, because ModalResult returns a nonzero value to the ShowModal function. However, because ModalResult returns mrCancel in one case, and mrOK in the other, your code can determine which button was pressed and branch accordingly.

To automatically close the dialog box when the user chooses a Cancel button or presses Esc,
Set the Cancel button's ModalResult property to mrCancel.

To automatically close the dialog box when the user presses Enter when an OK button has focus,
Set the button's ModalResult property to mrOK.

See Creating standard-command buttons for information on how to create in one simple step such standard command buttons as the Cancel and OK buttons just described.


Creating standard-command buttons

Section topics	See also

You can quickly create buttons for many standard commands by adding BitBtn components to the form and setting the Kind property for each button. The possible Kind property settings and their effect are shown in the following table. (In addition to the property settings shown, the bitmap button displays graphics, such as a green check mark for the OK button, or a red X for the Cancel button.)

Kind value	Effect	Appearance	Equivalent property setting(s)	Comments
bkAbort	Makes a Cancel button with Abort as caption		Caption := 'Abort'ModalResult := mrAbort	Red X appears next to caption.
bkAll	Creates an OK button (with All caption)		Caption := '&All'ModalResult := 8	Green double check mark appears next to caption.
bkCancel	Makes a Cancel button		Caption := 'Cancel'Cancel := TrueModalResult := mrCancel	Red X appears next to caption.
bkClose	Creates a Close button; closes the window		Caption := '&Close'	A lavender "exit" door appears as the glyph for this button.
bkCustom	None	N/A	N/A	Use this setting to create a custom command button, including specifying a custom Glyph bitmap.
bkHelp	Creates a button with Help as the caption		Caption := '&Help'	A blue ? appears next to the caption. Use the event handler of this button to call your program Help file. (If the dialog box has a Help context, Delphi does this automatically.)
bkIgnore	Creates a button to ignore changes and proceed with specified action		Caption := '&Ignore'ModalResult := mrIgnore	Use to continue an operation after an error condition has occurred.
bkNo	Makes a Cancel button (with No as the caption)		Caption := '&No'Cancel := TrueModalResult := mrNo	Red circle with slash appears next to caption.
bkOK	Creates an OK button		Caption := 'OK'Default := TrueModalResult := mrOK	Green check mark appears next to caption.
bkRetry	Creates a button to retry specified action		Caption := '&Retry'ModalResult := mrRetry	Green circular arrow appears next to the caption.
bkYes	Creates an OK button (with Yes caption)		Caption := '&Yes'Default := TrueModalResult := mrYes	Green check mark appears next to caption.

Note that setting the Kind property also sets the ModalResult property, discussed previously, in every case except bkCustom, bkHelp and bkClose. In these cases, ModalResult remains mrNone and choosing the button doesn't automatically close the dialog box.


Setting the tab order

Section topics	See also

Tab order is the order in which focus moves from component to component in a running application when the Tab key is pressed. To enable the Tab key to shift focus to a component on a form, the TabStop property of the component must be set to True.

The tab order is initially set by Delphi, corresponding to the order in which you add components to the form. You can change this by changing the TabOrder property of each component, or by using the Edit Tab Order dialog box.

To set tab order by using the Edit Tab Order dialog box:

1.	Select the form, or a container component in the form, that contains the components whose tab order you want to set.

2.	Choose Edit|Tab Order, or choose Tab Order from the form's SpeedMenu.

The Edit Tab Order dialog box appears, displaying a list of components ordered (first to last) in their current Tab order.

3.	Select a component in the Controls list, and press the appropriate arrow button (Up or Down), or drag the component to its new location in the tab order list.

4.	Choose OK.

Using the Edit Tab Order dialog box changes the value of the components' TabOrder property. You can also do this manually, if you want.

To manually change a component's TabOrder property:

1.	Select the component whose position in the tab order you want to change.

2.	In the Object Inspector, select the TabOrder property.

3.	Change the TabOrder property's value to reflect the position you want the component to have in the tab order.

Note:	The first component in the tab order should have the TabOrder value of 0.

Keep in mind the following points when manually setting your tab order (you needn't be concerned with these points if using the Edit Tab Order dialog box):

Each TabOrder property value must be unique. If you give a component a TabOrder value that has already been assigned to another component on this form, Delphi renumbers the TabOrder value for all other components accordingly.

If you attempt to give a component a TabOrder value equal to or greater than the number of components on the form (because numbering starts with 0), Delphi does not accept the new value, instead entering a value that ensures the component is last in the tab order.

Components that are invisible or disabled are not recognized in the tab order, even if they have a valid TabOrder value. When the user presses Tab, the focus skips over such components and goes to the next one in the tab order. For more information, see "Enabling and disabling components."


Testing the tab order

Section topics	See also

You can test the tab order you've set by running the application. At design time, focus always moves from component to component in the order that the components were placed on the form. Changes you make to the tab order at design time are reflected only at runtime.

To test the tab order:

1.	Run the application.

2.	Use the Tab key to view the order in which focus moves from component to component.


Removing a component from the tab order

Section topics	See also

In some cases you might want to prevent users from being able to tab to a component on a form. For example, there's usually no need for users to access a Panel component, because they can't modify it. You might want, in such a case, to remove the component from the tab order altogether. When the user presses the Tab key in the running application, the focus will skip over this component and go to the next one in the tab order. This is true even if the component has a valid TabOrder value.

To remove a component from the tab order:

1.	Select the component in the form.

2.	Use the Object Inspector to set the value of the TabStop property to False.

Note:	Removing a component from the tab order doesn't disable the component.


Enabling and disabling components

Section topics	See also

You often want to prevent a user from accessing certain components in a dialog box or form, either initially when the dialog box opens, or in response to changing conditions with the dialog box at runtime. For instance, in file-handling dialog boxes you might want to disable the OK button unless a file name is first specified.

When a component is disabled, it appears dimmed in the running application, and the user cannot tab to it, even if its TabStop property is set to True.

By disabling a component at design time, you specify that the component is initially unavailable to the user when the dialog box first opens. You can also dynamically change whether a component is enabled at runtime.

To disable a component at design time,
 Use the Object Inspector to set the component's Enabled property to False.

To disable a component at runtime,
Type the following code in an event handler for the component:

<componentn>.Enabled := False;

For example, the following event handler specifies that when Button1 receives an OnClick event, Button2 is disabled:

procedure TForm1.Button1Click(Sender: TObject);

begin

  Button2.Enabled := False;

end;


Setting the focus in a dialog box

Section topics	See also

Only one component per form can be active, or have the focus, in a running application at any given time. The component having the focus by default corresponds to the ActiveControl property of the form, which can be set at design time, or used during runtime to access the component that has focus. To change focus at runtime, use the SetFocus method.

Setting focus at design time

When you design a dialog box, you usually want to specify which component should have focus when the dialog box first opens. For example, in a data entry form you might want to give focus to the edit or memo component where users will first be entering data. That way the user can begin typing immediately, without first having to tab to a certain component.

To specify the active component at design time:

1.	Select the form, then use the Object Inspector to select the form's ActiveControl property.

2.	From the property's drop-down list, select the component you want to have focus when the form first opens.

If no component is specified as the form's active component, Delphi gives initial focus to the component that is first in the tab order, excluding

Disabled components

Components that are not visible at runtime

Components whose TabStop property is set to False

Setting focus at runtime

Focus changes automatically at runtime when the user tabs among components. You often want to actively specify a focus change. Taking the previous example, if you are designing a data entry form, you might want to specify that focus change from field to field each time the user presses an arrow key. You specify that a component gets focus by using the SetFocus method.

To change the active component during runtime,
Type the following code in the appropriate event handler, using the name of the component, in this example, Button1:

Button1.SetFocus;

For more information, see the online Help topic "SetFocus method."

Note:	If you set a button as the active control for the form at design time, that setting overrides at runtime any default button you might have specified. However, you'll often find ways to use an active control together with a default button to process user input.


Using dialog box routines

Section topics	See also

The Delphi Dialogs unit provides several simple routines that display predesigned dialog boxes to the user, based on the parameters you specify.

This section discusses how to use those routines to perform the following tasks in Delphi:

Displaying message boxes

Creating simple input forms

Masking input characters


Displaying message boxes

Section topics	See also

Message boxes display text to the user and must be manually cleared from the screen before the application can continue. Message boxes can be used for a variety of purposes, including:

Displaying warning information to the user

Displaying error messages

Requesting confirmation before closing a file or exiting the application

ShowMessage procedure

The ShowMessage procedure is the simplest way to display a brief message to the user. The syntax of the procedure is

ShowMessage(const Msg: string);

This procedure displays a modal dialog box containing the text you specify in the Msg parameter and an OK button that closes the dialog box.

For example, suppose you want to display a message box with the message Delphi delivers!. In a button's OnClick event handler, you would write the following code:

ShowMessage('Delphi delivers!');

When you run the program and click the button, a modal dialog box appears with the name of the executable file in the title bar, the text from the Msg parameter, and an OK button, as shown:

MessageDlg function

The MessageDlg function displays a message box that contains text you specify, and optionally, various captions, symbols, and buttons.

Here is the syntax of the function:

function MessageDlg(const Msg: string; AType: TMsgDlgType; AButtons: TMsgDlgButtons;
  HelpCtx: LongInt): Word;

This table summarizes what each parameter specifies:

Parameter name	Specifies	Comments
Msg	Text displayed in the dialog box	If you omit this parameter, no text appears in the dialog box.
AType	Caption on title bar, and accompanying symbol in dialog box	If you omit this parameter, no caption appears on the message box title bar.
AButtons	Type of button(s) in the dialog box	If you omit this parameter, no buttons appear in the box.
HelpCtx	Link to a Help topic	Help topic not required.

You need to pass an integer into the HelpCtx parameter even if you don't have an application Help file. In this case, pass 0 (zero). If you do have an application Help file, you should include a Help button in the dialog box and pass the Help context that corresponds to the topic you want users to view for this dialog box. If the integer you pass is not a valid Help context, users will get an error message when they choose the Help button.

For more information on connecting your application to a Help system, see "Application options."

MessageDlg example

To create a dialog box with the message Save changes? and OK, Cancel, and Help buttons, you would add this event handler to a button:

procedure TForm1.Button1Click(Sender: TObject);

begin

 MessageDlg('Save changes?', mtConfirmation, mbYesNoCancel, 0);

end;

When you run the program and click the button, the following message box appears:

Note:	MessageDlg displays the message box in the center of the screen by default. You can use the MessageDlgPos function to create a message box that appears at a screen location you specify.

For more information about MessageDlg and MessageDlgPos, look up the references in online Help and search the Help index for "messages."


Creating simple input forms

Section topics	See also

Where MessageDlg displays an informational form that requires only a button click from the user, the Dialogs unit also provides two functions, InputBox and InputQuery, that display forms requiring a single line of input from the user. For example, you might use such a form as a password entry screen.

Here is the syntax for each function:

function InputBox(const ACaption, APrompt, ADefault: string): string;

function InputQuery(const ACaption, APrompt: string; var Value: string): Boolean;

Both InputBox and InputQuery create a dialog box with OK and Cancel buttons. Use InputBox if you simply want the string the user enters returned if the user chooses OK, and not returned if the user chooses Cancel or presses Esc to exit the dialog box. Use InputQuery if you want to be able to interpret the user's action (OK, Cancel or Esc) as a Boolean variable that the program can recognize.

For an illustration of how InputBox works, suppose you have a Button1 and a Label1 component on a form and you write the following OnClick event handler for Button1:

procedure TForm1.Button1Click(Sender: TObject);

begin

 Label1.Caption := InputBox('Password Entry Form', 'Enter Password', '')

end;

Leaving the ADefault parameter string empty means no text initially appears in the input box.

If you run the program and choose Button1, the input box appears:

If you then type some text in the input box, and choose OK, the text appears in Label1. If, however, you choose Cancel or press Esc to exit the dialog box, the text is not displayed in Label1: instead, the ADefault string is returned. In this case, the string is blank, so the text in Label1's Caption property is cleared, but no other text replaces it.


Masking input characters

Section topics	See also

In a dialog box that you create yourself, you can mask the characters that a user enters into an edit field. Use the PasswordChar property of the edit box component to display any characters the user enters as special characters, such as asterisks (*).

If you look at the Password Dialog supplied with Delphi, you'll see that the PasswordChar property of the edit box component, Password, has already been set to *. When the user enters text in this dialog box at runtime, only the asterisk character is displayed, so that the user's password is not visible onscreen.

To see the Password entry Form Template at runtime, start with a new project, then:

1.	Add a button to Form1, and write the following OnClick event handler:

procedure TForm1.Button1Click(Sender: TObject);

begin

 PasswordDlg.ShowModal;

end;

2.	Add Password to the uses clause in Unit1, and run the application.

3.	Choose Button1 to display the Password dialog box.

4.	Type some text into the edit box.

Only asterisks appear.

Password dialog box displaying masked characters


Specifying forms to auto-create

Section topics	See also

The form you specify as the project main form is always "created" (loaded in memory) when the application runs. The main form is also the first form loaded unless you specify otherwise in code. As you create additional forms for the project, these are also auto-created at runtime.

This has the advantage of having all forms in the application available to display without writing any code to specifically create the form (you still need to show or hide the form). However, there might be times when you decide you don't want all the forms in an application created in memory when the application first starts running; you might prefer to control programmatically when the forms are created. For example, if there are several different forms that automatically connect to databases, you might prefer to create those forms only as necessary.

You can use the Project Options dialog box to specify which of your application's forms will auto-create at runtime.

To specify whether forms are auto-created at runtime:

1.	Choose Project|Options, and select the Forms page.

The names of all forms in the project are displayed in the Auto-Create Forms list.

2.	In the Auto-Create Forms list, select any form(s) that you do not want created in memory at runtime, and choose the > button. To move all form names from one list to the other, choose the << or the >> button.

The selected files move to the Available Forms list. Forms displayed in this list do not auto-create at runtime.

3.	Choose OK.

Note:	It's usually best to have Delphi create your application forms. If you decide not to auto-create a form, you must specifically create the forms at runtime by writing code. If you try to reference a form that hasn't first been created, for example by calling its Show method, Delphi raises an exception.

For more information on creating forms at runtime, search the online Help index for "instantiating forms."


Controlling the form auto-create order

Section topics	See also

After the main form, the forms you specify in the Auto-Create Forms list are created at runtime in the order they are displayed in the list. You can change the order by changing a form name's position in the list. For instance, if you've designed one form that contains dependencies to another form, you can ensure the form is available by creating it first.

To change a form's creation order,
In the Auto-Create list, select the form name and drag it to the position you want.

Note:	The main form and auto-create specifications on the Forms page of the Project Options dialog box are reflected in the Application.CreateForm statements in the project (.DPR) file.


MDI and SDI forms

Section topics	See also

Any form you design can be implemented in your application as a multiple document interface (MDI) or single document interface (SDI) form. In an MDI application, more than one document or child window can be opened within a single parent window. This is common in applications such as spreadsheets or word processors. An SDI application, by contrast, normally contains a single document view.

It's probably more common to design an MDI form as such from the beginning, but you can change forms you create into MDI or SDI forms simply by changing their FormStyle properties.

The following table displays the possible FormStyle property settings and their purpose.

FormStyle value	Creates	Comments
fsNormal	A standalone (SDI) form
fsMDIForm	An MDI parent or frame form	This form can contain other forms at runtime.
fsMDIChild	An MDI child form	This form is contained by the MDI parent at runtime.
fsStayOnTop	A stand-alone form that stays on top of other open forms at runtime

Sample MDI child and parent forms:

You can resize and move either form, but the child form always stays within the borders of the parent.

For more information about MDI applications, see "Creating an MDI application."


See also

Specifying the default new form

Specifying the default main form

Forms, the basic building blocks

Form keyboard shortcuts

Alignment dialog box

About the Component palette

Using Windows common dialogs

Buttons and similar controls

Creating an MDI application


Using data modules

Section topics	See also

A data module is a specialized Delphi class for centralized handling of any nonvisual component in an application. Typically these are data access components (TSession, TDatabase, TTable, TQuery, TStoredProc, and TBatchMove), but they can also be other nonvisual components (TTimer, TOpenDialog, TSaveDialog, and TImageList, for example). A data module enables you to

Place all your data access components in a single visual container at design time instead of duplicating them on each application form.

Design tables and queries once for use with many forms instead of recreating them separately for each form.

Create business rules--using component events, and additional methods you add to the unit code for a data module--that can be shared across an entire application.

Separate business logic and data access from user interface code for easier maintenance.

Standardize common dialogs, timers, DDE client, DDE client items, DDE server, DDE server items, and image lists across an application.

Store well-designed data access modules in the Object Repository to share with other projects and developers.


Using standard data modules and remote data modules

Section topics	See also

There are two types of data modules: standard and remote. If you create single- or two-tiered applications, you will always use a standard data module. If you have the Delphi Client/Server Suite, and you are creating a multi-tiered database application, then you should add a remote data module to your application server. To learn how to add a remote data module to your project, see "Adding a remote data module to an application server project."


Creating a new data module

Section topics	See also

To create a new, empty data module for a project, choose File|New Data Module. Delphi opens a data module container on the desktop and adds the data module to the project file.

At design time a data module looks like a standard Delphi form with a white background and no alignment grid. As with forms, you can place nonvisual components on a module from the Components palette, and you can resize a data module to accommodate the components you add to it. You can also right-click a module to display a context menu for it. The following table summarizes the context menu options for a data module:

Menu item	Purpose
Align To Grid	Aligns data access components to the module's invisible grid.
Align	Aligns data access components according to criteria you supply in the Alignment dialog box.
Revert to Inherited	Discards changes made to a module inherited from another module in the Object Repository, and reverts to the originally inherited module.
Creation Order	Enables you to change the order in which data access components are created at start-up.
Add to Repository	Stores a link to the data module in the Object Repository.
View as Text	Displays the text representation of the data module's properties.

Behind the data module container, there is a corresponding unit file containing source code for the data module.


Naming a data module and its unit file

Section topics	See also

The title bar of a data module displays the module's name. The default name for a data module is "DataModuleN" where N is a number representing the lowest unused unit number in a project. For example, if you start a new project, and add a module to it before doing any other application building, the name of the module defaults to "DataModule2". The corresponding unit file for DataModule2 defaults to "Unit2".

You should rename your data modules and their corresponding unit files at design time to make them more descriptive. You should especially rename data modules you add to the Object Repository to avoid name conflicts with other data modules in the Repository or in applications that use your modules.

To rename a data module,

1.	Select the module.

2.	Edit the Name property for the module in the Object Inspector.

The new name for the module appears in the title bar when the Name property in the Object Inspector no longer has focus.

Changing the name of a data module at design time changes its variable name in the interface section of code. It also changes any use of the type name in procedure declarations. You must manually change any references to the data module in code you write.

To rename a unit file for a data module,

1.	Select the unit file.

2.	Choose File|SaveAs.

3.	In the Save As dialog box, enter a file name that clearly identifies the unit with the renamed data module.


Placing and naming components

Section topics	See also

You place nonvisual components, such as TTable and TQuery, in a data module just as you place visual components on a form. Click the desired component on the appropriate page of the Component palette, then click in the data module to place the component. You cannot place visible controls, such as grids, on a data module. If you attempt it, you receive an error message.

For ease of use, components are displayed with their names in a data module. When you first place a component, Delphi assigns it a generic name that identifies what kind of component it is, such as DataSource1 and Table1. This makes it easy to select specific components whose properties and methods you want to work with. To make it even easier, you should give your components more descriptive names (for example, CustSource and CustTable).

To change the name of a component in a data module,

1.	Select the component.

2.	Edit the component's Name property in the Object Inspector.

The new name for the component appears under its icon in the data module as soon as the Name property in the Object Inspector no longer has focus.

When you name a component, the name you give it should reflect the type of component and what it is used for. For example, for database components, the name should reflect the type of component, and the database it accesses. For example, suppose your application uses the CUSTOMER table. To access CUSTOMER you need a minimum of two data access components: a data source component and a table component. When you place these components in your data module, Delphi assigns them the names DataSource1 and Table1. To reflect that these components use CUSTOMER, and to relate the components to one another, you could change these names to CustSource and CustTable.


Using component properties and methods in a data module

Section topics	See also

Placing components in a data module centralizes their behavior for your entire application. For example, you can use the properties of dataset components, such as TTable and TQuery, to control the data available to the data source components that use those datasets. Setting the ReadOnly property to True for a dataset prevents users from editing the data they see in a data-aware visual control on a form. You can also invoke the Fields editor for a dataset to restrict the fields within a table or query that are available to a data source and therefore to the data-aware controls on forms.

The properties you set for components in a data module apply consistently to all forms in your application that use the module.

In addition to properties, you can write event handlers for components. For example, a TDataSource component has three possible events: OnDataChange, OnStateChange, and OnUpdateData. A TTable component has over 20 potential events. You can use these events to create a consistent set of business rules that govern data manipulation throughout your application.


Creating business rules in a data module

Section topics	See also

Besides writing event handlers for the components in a data module, you can code methods directly in the unit file for a data module. These methods can be applied to the forms that use the data module as business rules. For example, you might write a procedure to perform month-, quarter-, or year-end bookkeeping. You might call the procedure from an event handler for a component in the data module.

The prototypes for the procedures and functions you write for a data module should appear in the module's type declaration:

type

 TCustomerData = class(TDataModule)

   Customers: TTable;

   Orders: TTable;

   ...

 private

   { Private declarations }

 public

   { Public declarations }

   procedure LineItemsCalcFields(DataSet: TDataSet); { A procedure you add }

 end;

var

 CustomerData: TCustomerData;

The procedures and functions you write should follow in the implementation section of the code for the module.


Adding a remote data module to an application server project

Section topics	See also

If you have the Delphi Client/Server Suite and you are building a multi-tiered database application, you can add a remote data module to your application server project. A remote data module is an ActiveX-enabled data module that makes an application server into an OLE Automation object that you can access remotely from client machines.

1.	To add a remote data module to a project,

2.	Choose File|New.

3.	Select the Business page in the New Items dialog box.

4.	Choose Remote Data Module.

Once you place a remote data module in a project, you use it just like a standard data module. OLE Automation and registration is automatic.


Reusing data modules in the Object Repository

Section topics	See also

Instead of creating a data module from scratch, you may be able to reuse an existing data module from the Object Repository. See "Sharing objects," which discusses the process, including the Copy, Inherit, and Use options availble to you. In addition, see the following topics on copying, inheriting, and using data modules.


Copying a data module

Section topics	See also

Copy a data module from the repository when

The module already provides a fundamental level of access to data your application needs,

The demands of your application require substantial changes and additions to the components in the module, and

You do not want changes made to the ancestor data module in the repository to affect the module in your application.

Note:	If you want future changes made to ancestor data modules in the repository to ripple into your project, you should inherit the data module instead of copying it.


Inheriting a data module

Section topics	See also

Inherit a data module from the repository when

The module provides a fundamental level of access to data your application needs, and

You want to propagate changes made to the ancestor data module in the repository to the module in your application (for example, because you want to maintain a consistent corporate view of data in all your applications).

If you add components and event handlers to your copy of an inherited data module, you generate new code only in your application for the added components and event handlers.

Note:	Do not inherit data modules that contain TSession or TDatabase components. You should copy these modules instead of inheriting them. A second option is to put TSession and TDatabase components in a separate data module that you can use, and to put all other components in another data module that you can inherit.

Inheriting event handlers

If you override an event handler for a component inherited from a data module (for example, to provide functionality specific to your application), you can still invoke the inherited handler from your new handler. When you override the handler, the new handler is created with a single statement: inherited. This call invokes the event handler from the ancestor data module if it exists. If it does not exist, the inherited statement is ignored. If you do not want to call an inherited handler from your new handler, delete the inherited statement.


"Using" a data module

Section topics	See also

You can both copy a data module from the Object Repository into your application and make changes to it that replace the original module in the repository by selecting the Use radio button in the New Items dialog box.

"Use" a data module in the repository when

The module already provides a fundamental level of access to data your application needs,

The demands of your application require substantial changes and additions to the components in the module, and

The changes you need to make in your application are also fundamentally applicable to other applications.

Important:	Be careful with the Use radio button option. Make sure the changes you make to a data module are robust and thoroughly tested before letting others copy it into their applications from the repository.


Accessing a data module from a form

Section topics	See also

To associate visual controls on a form with a data module, you must first add the data module to the form's uses clause. To add a module to a form's uses clause, you can:

Choose File|Use Unit and enter the name of the module or pick it from the list box in the Use Unit dialog box, or

Drag selected field from the Fields editor of a TTable or TQuery component in the data module to a form. In this case Delphi prompts you to confirm that you want to add the module to the form's uses clause, then creates controls based on data dictionary information for each field you dragged into the form.

Before you drag fields to a form, set the data source for the fields you intend to drag. Delphi uses an existing data source in the data module if it is already connected to the dataset.

If a data source is not defined, Delphi adds a new data source component to the form and sets the DataSource properties of the controls it creates to point to this data source. The data source itself is associated with a table or query component in the data module. Should this happen, you can keep this arrangement, or, to keep your data access model cleaner, you can change it. Delete the data source component on the form, and set the DataSource properties of the control on the form to point directly to the appropriate data source in the data module.


See also

Unit files

Creating a multi-tiered application

ActiveX Web deployment

Copy, inherit, or use?


Using the Menu Designer

Section topics	See also

Menus provide an easy way for your users to execute logically grouped commands. The Delphi Menu Designer enables you to easily add a menu--either predesigned or custom tailored--to your form. You simply add a menu component to the form, open the Menu Designer, and type menu items directly into the Menu Designer window. You can add or delete menu items, or drag and drop them to rearrange them during design time.

You don't even need to run your program to see the results--your design is immediately visible in the form, appearing just as it will during runtime. Your code can also change menus at runtime, to provide more information or options to the user.

These topics explain how to use the Delphi Menu Designer to design menu bars and pop-up (local) menus. It discusses the following ways to work with menus at design time and runtime:

Opening the Menu Designer

Building menus

Editing menu items in the Object Inspector

Using the Menu Designer context menu

Using menu templates

Saving a menu as a template

Note:	For specific code samples that demonstrate ways to manipulate menus at runtime, refer to "Creating an MDI application."


Opening the Menu Designer

Section topics	See also

To start using the Delphi Menu Designer, first add either a MainMenu or PopupMenu component to your form. Both menu components are located on the Standard page of the Component palette:

A MainMenu component creates a menu that's attached to the form's title bar. A PopupMenu component creates a menu that appears when the user right-clicks in the form (that is, a context menu). Pop-up menus do not have a menu bar.

To open the Menu Designer,
Select a menu component on the form, and then choose from one of the following methods:

Double-click the menu component.

From the Properties page of the Object Inspector, select the Items property, and then either double-click [Menu] in the Value column, or click the ellipsis (...) button.

The Menu Designer appears, with the first (blank) menu item highlighted in the Designer, and the Caption property highlighted in the Object Inspector.

Menu Designer for a main menu:

Menu Designer for a pop-up menu:


Building menus

Section topics	See also

You add a menu component to your form, or forms, for every menu you want to include in your application. You can build each menu structure entirely from scratch, or you can start from one of the Delphi predesigned menu templates.

These topics discuss the basics of creating a menu at design time. For more information about Delphi menu templates, see "Using menu templates."


Naming menus

Section topics	See also

As with all components, when you add a menu component to the form, Delphi gives it a default name; for example, MainMenu1. You can give the menu a more meaningful name that follows Object Pascal naming conventions. (For more detail on the Object Pascal language, see "Writing Object Pascal code.")

Delphi adds the menu name to the form's type declaration, and the menu name then appears in the Component list.


Naming the menu items

Section topics	See also

In contrast to the menu component itself, you need to explicitly name menu items as you add them to the form. You can do this in one of two ways:

Directly type in the value for the Name property.

Type in the value for the Caption property first, and let Delphi derive the Name property from the caption.

For example, if you give a menu item a Caption property value of File, Delphi assigns the menu item a Name property of File1. If you fill in the Name property before filling in the Caption property, Delphi leaves the Caption property blank until you type in a value.

Note:	If you enter characters in the Caption property that are not valid for Object Pascal identifiers, Delphi modifies the Name property accordingly. For example, if you want the caption to start with a number, Delphi precedes the number with a character to derive the Name property.

The following table demonstrates some examples of this, assuming all menu items shown appear in the same menu bar.

Component caption	Derived name	Explanation
&File	File1	Removes ampersand
&File (2nd occurrence)	File2	Numerically orders duplicate items
1234	N12341	Adds a preceding letter and numerical order
1234 (2nd occurrence)	N12342
$@@@#	N1	Removes all non-standard characters, adding preceding letter and numerical order
- (hyphen)	N2	Numerical ordering of second occurrence of caption with no standard characters

As with the menu component, Delphi adds any menu item names to the form's type declaration, and those names then appear in the Component list.


Adding, inserting, and deleting menu items

Section topics	See also

The following procedures describe how to perform the basic tasks involved in building your menu structure. Each procedure assumes you have the Menu Designer window open.

To add menu items at design time:

1.	Select the position where you want to create the menu item.

If you've just opened the Menu Designer, the first position on the menu bar is already selected.

2.	Begin typing to enter the caption. Or enter the Name property first by specifically placing your cursor in the Object Inspector and entering a value. In this case, you then need to reselect the Caption property and enter a value.

3.	Press Enter.

The next placeholder for a menu item is selected.

If you entered the Caption property first, use the arrow keys to return to the menu item you just entered. You'll see that Delphi has filled in the Name property based on the value you entered for the caption. (See "Naming the menu items.")

4.	Continue entering values for the Name and Caption properties for each new item you want to create, or press Esc to return to the menu bar.

Use the arrow keys to move from the menu bar into the menu, and to then move between items in the list; press Enter to complete an action. To return to the menu bar, press Esc.

To insert a new, blank menu item:

1.	Place the cursor on a menu item.

2.	Press Ins.

Menu items are inserted to the left of the selected item on the menu bar, and above the selected item in the menu list.

To delete a menu item or command:

1.	Place the cursor on the menu item you want to delete.

2.	Press Del.

Note:	You cannot delete the default placeholder that appears below the item last entered in a menu list, or next to the last item on the menu bar. This placeholder does not appear in your menu at runtime.

Adding separator bars

Separator bars insert a line between menu items. You can use separator bars to indicate groupings within the menu list, or simply to provide a visual break in a list.

To make the menu item a separator bar,
Type a hyphen (-) for the caption.

Specifying accelerator keys and keyboard shortcuts

Accelerator keys enable the user to access a menu command from the keyboard by pressing Alt+ the appropriate letter, indicated in your code by the preceding ampersand. The letter after the ampersand appears underlined in the menu.

Keyboard shortcuts enable the user to perform the action without accessing the menu directly, by typing in the shortcut key combination.

To specify an accelerator,
Add an ampersand in front of the appropriate letter.

For example, to add a Save menu command with the S as an accelerator key, type &Save.

To specify a keyboard shortcut,
Use the Object Inspector to enter a value for the ShortCut property, or select a key combination from the drop-down list.

This list is only a subset of the valid combinations you can type in.

When you add a shortcut, it appears next to the menu item caption.

Caution:	Delphi does not check for duplicate shortcut keys or accelerators, so you need to track values you have entered in your application menus.


Creating submenus

Section topics	See also

Many application menus contain drop-down lists that appear next to a menu item to provide additional, related commands. Such lists are indicated by an arrow to the right of the menu item. Delphi supports as many levels of such submenus as you want to build into your menu.

Organizing your menu structure this way can save vertical screen space. However, for optimal design purposes you probably want to use no more than two or three menu levels in your interface design. (For pop-up menus, you might want to use only one submenu, if any.)

To create a submenu:

1.	Select the menu item under which you want to create a submenu.

2.	Press CtrlÆ to create the first placeholder, or choose Create Submenu from the context menu.

3.	Type a name for the submenu item, or drag an existing menu item into this placeholder.

4.	Press Enter, or Ø, to create the next placeholder.

5.	Repeat steps 3 and 4 for each item you want to create in the submenu.

6.	Press Esc to return to the previous menu level.

Creating submenus by demoting existing menus

You can create a submenu by inserting a menu item from the menu bar (or a menu template) between menu items in a list. When you move a menu into an existing menu structure, all its associated items move with it, creating a fully intact submenu. This pertains to submenus as well--moving a menu item into an existing submenu just creates one more level of nesting.

Moving menu items

During design time, you can move menu items simply by dragging and dropping. You can move menu items along the menu bar, or to a different place in the menu list, or into a different menu entirely.

The only exception to this is hierarchical: you cannot demote a menu item from the menu bar into its own menu; nor can you move a menu item into its own submenu. However, you can move any item into a different menu, no matter what its original position is.

While you are dragging, the cursor changes shape to indicate whether you can release the menu item at the new location. When you move a menu item, any items beneath it move as well.

To move a menu item along the menu bar:

1.	Drag the menu item along the menu bar until the arrow tip of the drag cursor points to the new location.

2.	Release the mouse button to drop the menu item at the new location.

To move a menu item into a menu list:

1.	Drag the menu item along the menu bar until the arrow tip of the drag cursor points to the new menu.

This causes the menu to open, enabling you to drag the item to its new location.

2.	Drag the menu item into the list, releasing the mouse button to drop the menu item at the new location.


Viewing the menu

Section topics	See also

You can view your menu in the form at design time without first running your program code. (Pop-up menu components are visible in the form at design time, but the pop-up menus themselves are not. Use the Menu Designer to view a pop-up menu at design time.)

To view the menu:

1.	If the form is visible, click the form, or from the View menu, choose the form whose menu you want to view.

2.	If the form has more than one menu, select the menu you want to view from the form's Menu property drop-down list.

The menu appears in the form exactly as it will when you run the program.


Editing menu items in the Object Inspector

Section topics	See also

These topics have discussed how to set several properties for menu items--for example, the Name and Caption properties--by using the Menu Designer.

These topics have also described how to set menu item properties, such as the ShortCut property, directly in the Object Inspector, just as you would for any component selected in the form.

When you edit a menu item by using the Menu Designer, its properties are still displayed in the Object Inspector. You can switch focus to the Object Inspector and continue editing the menu item properties there. Or you can select the menu item from the Component list in the Object Inspector and edit its properties without ever opening the Menu Designer.

To close the Menu Designer window and continue editing menu items:

1.	Switch focus from the Menu Designer window to the Object Inspector by clicking the properties page of the Object Inspector.

2.	Close the Menu Designer as you normally would.

The focus remains in the Object Inspector, where you can continue editing properties for the selected menu item. To edit another menu item, select it from the Component list.


Using the Menu Designer context menu

Section topics	See also

The Menu Designer context menu provides quick access to the most common Menu Designer commands, and to the menu template options. (For more information about menu templates, refer to "Using menu templates.")

To display the context menu,
Right-click the Menu Designer window, or press Alt+F10 when the cursor is in the Menu Designer window.


Commands on the context menu

Section topics	See also

The following table summarizes the commands on the Menu Designer context menu.

Menu command	Action
Insert	Inserts a placeholder above or to the left of the cursor.
Delete	Deletes the selected menu item (and all its sub-items, if any).
Create Submenu	Creates a placeholder at a nested level and adds an arrow to the right of the selected menu item.
Select Menu	Opens a list of menus in the current form. Double-clicking a menu name opens the designer window for the menu.
Save As Template	Opens the Save Template dialog box, where you can save a menu for future reuse.
Insert From Template	Opens the Insert Template dialog box, where you can select a template to reuse.
Delete Templates	Opens the Delete Templates dialog box, where you can choose to delete any existing templates.
Insert From Resource	Opens the Insert Menu from Resource file dialog box, where you can choose an .MNU file to open in the current form.


Switching among menus at design time

Section topics	See also

If you're designing several menus for your form, you can use the Menu Designer context menu or the Object Inspector to easily select and move among them.

To use the context menu to switch among menus in a form:

1.	Right click in the Menu Designer to display the context menu.

2.	From the context menu, choose Select Menu.

The Select Menu dialog box appears.

This dialog box lists all the menus associated with the form whose menu is currently open in the Menu Designer.

3.	From the list in the Select Menu dialog box, choose the menu you want to view or edit.

To use the Object Inspector to switch among menus in a form:

1.	Give focus to the form whose menus you want to choose from.

2.	From the Component list, select the menu you want to edit.

3.	On the Properties page of the Object Inspector, select the Items property for this menu, and then either click the ellipsis button, or double-click [Menu].


Using menu templates

Section topics	See also

Delphi provides several predesigned menus, or menu templates, that contain frequently used commands. You can use these menus in your applications without modifying them (except to write code), or you can use them as a starting point, customizing them as you would a menu you originally designed yourself. Menu templates do not contain any event handler code.

The menu templates shipped with Delphi are stored in the BIN subdirectory in a default installation. These files have a .DMT (Delphi menu template) extension.

You can also save as a template any menu that you design using the Menu Designer. After saving a menu as a template, you can use it as you would any predesigned menu. If you decide you no longer want a particular menu template, delete it from the list.

To add a menu template to your application:

1.	Right-click the Menu Designer window.

The Menu Designer context menu appears.

2.	From the context menu, choose Insert From Template.

(If there are no templates, the Insert From Template option appears dimmed in the context menu.)

The Insert Template dialog box opens, displaying a list of available menu templates.

3.	Select the menu template you want to insert, then press Enter or choose OK.

This inserts the menu into your form at the cursor's location. For example, if your cursor is on a menu item in a list, the menu template is inserted above the selected item. If your cursor is on the menu bar, the menu template is inserted to the left of the cursor.

To delete a menu template:

1.	Right-click the Menu Designer window.

The Menu Designer context menu appears.

2.	From the context menu, choose Delete Templates.

(If there are no templates, the Delete Templates option appears dimmed in the context menu.)

The Delete Templates dialog box opens, displaying a list of available templates.

3.	Select the menu template you want to delete, and press Del.

Delphi deletes the template from the templates list and from your hard disk.


Saving a menu as a template

Section topics	See also

Any menu you design can be saved as a template so you can use it again. You can use menu templates to provide a consistent look to your applications, or use them as a starting point which you then further customize.

The menu templates you save are stored in your BIN subdirectory as .DMT files.

To save a menu as a template:

1.	Design the menu you want to be able to reuse.

This menu can contain as many items, commands, and submenus as you like; everything in the active Menu Designer window will be saved as one reusable menu.

2.	Right-click in the Menu Designer window to open the context menu.

3.	From the Menu Designer context menu, choose Save As Template.

The Save Template dialog box appears.

4.	In the Template Description edit box, type a brief description for this menu, and then choose OK.

The Save Template dialog box closes, saving your menu design and returning you to the Menu Designer window.

Note:	The description you enter is displayed only in the Save Template, Insert Template, and Delete Templates dialog boxes. It is not related to the Name or Caption property for the menu.


Naming conventions for template menu items and event handlers

Section topics	See also

When you save a menu as a template, Delphi does not save its Name property, since every menu must have a unique name within the scope of its owner (the form). However, when you insert the menu as a template into a new form by using the Menu Designer, Delphi then generates new names for it and all of its items.

For example, suppose you save a File menu as a template. In the original menu, you name it MyFile. If you insert it as a template into a new menu, Delphi names it File1. If you insert it into a menu with an existing menu item named File1, Delphi names it File2.

Delphi also does not save any OnClick event handlers associated with a menu saved as a template, since there is no way to test whether the code would be applicable in the new form. When you generate a new event handler for the menu template item, Delphi still generates the event handler name.

You can easily associate items in the menu template with existing OnClick event handlers in the form. For more information, see "Associating a menu item with an existing event handler."


Manipulating menu items at runtime

Section topics	See also

Sometimes you want to add menu items to an existing menu structure while the application is running, to provide more information or options to the user. You can insert a menu item by using the menu item's Add or Insert method, or you can alternately hide and show the items in a menu by changing their Visible property. The Visible property determines whether the menu item is displayed in the menu. To dim a menu item without hiding it, use the Enabled property.

For specific code examples that use the menu item's Visible and Enabled properties, see "Disabling menu items."

In multiple document interface (MDI) and Object Linking and Embedding (OLE) applications, you can also merge menu items into an existing menu bar.


Merging menus

Section topics	See also

For MDI applications, such as the text editor sample application, and for OLE client applications, your application's main menu needs to be able to receive menu items either from another form or from the OLE server object. This is often called merging menus.

To prepare menus for merging, specify values for two properties:

Menu, a property of the form

GroupIndex, a property of menu items in the menu


Specifying the active menu: Menu property

Section topics	See also

The Menu property specifies the active menu for the form. Menu-merging operations apply only to the active menu. If the form contains more than one menu component, you can change the active menu at runtime by setting the Menu property in code. For example,

Form1.Menu := SecondMenu;


Determining the order of merged menu items: GroupIndex property

Section topics	See also

The GroupIndex property determines the order in which the merging menu items appear in the shared menu bar. Merging menu items can replace those on the main menu bar, or can be inserted.

The default value for GroupIndex is 0. Several rules apply when specifying a value for GroupIndex:

Lower numbers appear first (farther left) in the menu.

For instance, set the GroupIndex property to 0 (zero) for a menu that you always want to appear leftmost, such as a File menu. Similarly, specify a high number (it needn't be in sequence) for a menu that you always want to appear rightmost, such as a Help menu.

To replace items in the main menu, give items on the child menu the same GroupIndex value.

This can apply to groupings or to single items. For example, if your main form has an Edit menu item with a GroupIndex value of 1, you can replace it with one or more items from the child form's menu by giving them a GroupIndex value of 1 as well.

Giving multiple items in the child menu the same GroupIndex value keeps their order intact when they merge into the main menu.

To insert items without replacing items in the main menu, leave room in the numeric range of the main menu's items and "plug in" numbers from the child form.

For example, number the items in the main menu 0 and 5, and insert items from the child menu by numbering them 1, 2, 3 and 4.


Importing resource (.RC) files

Section topics	See also

Delphi supports menus built with other applications, so long as they are in the standard Windows resource (.RC) file format. You can import such menus directly into your Delphi project, saving you the time and effort of rebuilding menus that you created elsewhere.

To load existing .RC menu files:

1.	In the Menu Designer, place your cursor where you want the menu to appear.

The imported menu can be part of a menu you are designing, or an entire menu in itself.

2.	From the Menu Designer context menu, choose Insert From Resource.

The Insert Menu From Resource dialog box appears.

3.	In the dialog box, select the resource file you want to load, and choose OK.

The menu appears in the Menu Designer window.

Note:	If your resource file contains more than one menu, you first need to save each menu as a separate resource file before importing it.


See also

Associating menu events with code

Resource files

Creating an MDI application

Using menu templates


Working with event handlers

Section topics	See also

In Delphi, almost all the code you write is executed, directly or indirectly, in response to events. Such code is called an event handler. Event handlers are actually specialized procedures. These topics show how to:

Generating the default event handler

Generating a new event handler

Locating event handlers

Associating an event with an existing event handler

Associating menu events with code


Generating the default event handler

Section topics	See also

The default event is the one the component most commonly needs to handle at runtime. For example, a button's default event is the OnClick event.

To generate a default event handler, if one exists, double-click the component in the form. The Code editor opens, with the cursor placed exactly where you need to be to modify the event handler.

Note:	Certain components, such as the Bevel, don't need to handle user events. For these components, double-clicking them in the form doesn't generate an event handler. Double-clicking certain other components, such as the Image component or either of the menu components, opens a dialog box where you perform design-time property edits.

Another way to generate a default event handler is to use the Object Inspector: On the Events page, double-click an event's value column.


Generating a new event handler

Section topics	See also

You can use the Object Inspector to generate an event handler for the form, or any component you have on the form. When you use the Object Inspector, Delphi generates and maintains parts of the code for you. The first line of the event handler names the procedure and specifies the parameters it uses. The code you write between the begin..end block will execute whenever the event occurs.

To use the Object Inspector to generate an event handler:

1.	Select a component (or the form).

2.	Click the Events tab on the Object Inspector.

The Events page of the Object Inspector displays all events that can be associated with the component, with the default event highlighted.

3.	Select the event you want, then double-click the Value column, or press Ctrl+Enter.

Delphi generates the event handler in the Code editor and places the cursor inside the begin..end block.

4.	Inside the begin..end block, type the code that you want to execute when the event occurs.

In Delphi, you usually use the Object Inspector to generate event handlers. Of course, you can write source code in the Code editor without using the Object Inspector. For example, you might write a general-purpose routine that's not associated directly with a component event, but that's called by an event handler. You can also write event handlers manually, but any time you can use the Object Inspector, you should, because the Object Inspector not only generates the event handler name for you, but if you change that name later by using the Object Inspector, Delphi changes it everywhere that it occurs in your source code. This is not the case for event handlers you write without using the Object Inspector.


Locating event handlers

Section topics	See also

If you generated a default event handler for a component by double-clicking it in the form, you can locate that event handler in the same way: double-click the component in the form.

The Code editor opens, with your cursor placed at the start of the first line of code for the component's default event handler.


Locating an event handler that's not the default

Section topics	See also

To locate an event handler that's not the default:

1.	In the form, select the component whose event handler(s) you want to locate.

2.	In the Object Inspector, click the Events tab.

3.	Double-click the event whose code you want to view.

The Code editor opens, with the cursor placed inside the begin..end block of the event handler. You can now make modifications.


Associating an event with an existing event handler

Section topics	See also

You can reuse code by writing event handlers that handle more than one component event. You can do this in the case of generic event handlers that, for example, are called by both a menu command and an equivalent button on a SpeedBar. Once you write one such event handler, you can easily associate other events with the original handler.

To associate another component's event with an existing event handler:

1.	In the form, select the component whose event you want to code.

2.	On the Events page of the Object Inspector, select the event to which you want to attach the handler code.

3.	Click the down arrow next to the event to open a list of previously written event handlers.

The list shows only the event handlers in this form that can be assigned to the selected event.

4.	Select from the list by clicking an event-handler name.

The code written for this event handler is now associated with the selected component event.

Note:	Delphi does not duplicate the event handler code for every component event associated with a shared handler. You see the code for shared handlers in the Code editor only once, even though the same code is called whenever the associated component events occurs.


Using the Sender parameter

Section topics	See also

You can also have several different components share a handler that does different things depending on which component called it. To do so, use the Sender parameter in an if..then..else statement.

The Sender parameter in an event handler informs Delphi which component received the event, and therefore called the handler.

The following code either displays the title of the application in the caption of a modal dialog box, or else displays nothing, depending on whether the OnClick event was received by Button1 or by another button using the same event handler:

procedure TMain1.Button1Click(Sender: TObject);

begin

if Sender = Button1 then

 AboutBox.Caption := 'About ' + Application.Title

else AboutBox.Caption := '';

AboutBox.ShowModal;

end;

If you run the program and click Button1, the application title appears in the About box caption. If you click any other component associated with this event handler, the About box opens but does not display the application title.


Displaying and coding shared events

Section topics	See also

There is another way to share event handlers. When components have events in common, you can select the components to display their shared events, select a shared event, and then create a new event handler for it, or select an existing one. All the selected components will now use this event handler.

To display and code shared events:

1.	In the form, select (Shift-click) all the components whose common events you want to view.

2.	On the Events page of the Object Inspector, select the event.

The Object Inspector displays only those events that the selected components have in common. (Only events in the current form are displayed.)

To associate an event that several components have in common with an existing event handler:

1.	Select the components.

2.	On the Events page of the Object Inspector, select the event.

The Object Inspector displays only those events that the selected components have in common.

3.	From the drop-down list next to the event, select an existing event handler, and press Enter.

Whenever any of the components you selected receives the specified event, the event handler you selected is called.

To create an event handler for a shared event:

1.	Select the components for which you want to create a shared event handler.

2.	On the Events page of the Object Inspector, select an event.

3.	Type a name for the new event handler and press Enter, or double-click the Handler column if you want Delphi to generate a name.

Delphi creates an event handler in the Code editor, positioning the cursor in the begin..end block.

If you choose not to name the event handler, Delphi names it for you based on the order in which you selected the components. For example, if you create a shared event handler for several buttons, Delphi names the event handler Button<n>Click, where Button<n> is the first button you selected for sharing an event handler.

4.	Type the code you want executed when the selected event occurs for any of the components.


Modifying a shared event handler

Section topics	See also

Modifying an event handler that is shared by more than one component is virtually the same as modifying any existing event handler. Just remember that whenever you modify a shared event handler, you are modifying it for all the component events that call it.

To modify the shared event handler:

1.	Select any of the components whose event calls the handler you want to modify.

2.	On the Events page of the Object Inspector, double-click the event handler name.

3.	In the Code editor, modify the handler.

Now when any of the components that share this handler receive the shared event, the modified code gets called.

For more information about sharing forms and projects, see "Sharing objects."


Deleting event handlers

Section topics	See also

When you delete a component, Delphi removes its reference in the form's type declaration. However, deleting a component does not delete any associated methods from the unit file, because as noted earlier, those methods might be called by other components in the form. You can still run your application so long as the method declaration and the method itself both remain in the unit file. If you delete the method without deleting its declaration, Delphi generates an "Undefined forward" error message, indicating that you need to either replace the method itself (if you intend to use it), or delete its declaration as well as the method code (if you do not intend to use it).

You can still explicitly delete an event handler, if you choose, or you can have Delphi do it for you.

To manually delete an event handler,
Remove all event handler code and the handler's declaration.

To have Delphi delete an event handler,
Remove all the code (including comments) inside the event handler. The handler is removed when you compile or save the project.


Associating menu events with code

Section topics	See also

While you use the Delphi Menu Designer to visually design your application menus, the underlying code is what makes the menus useful. Each menu command needs to be able to respond to an OnClick event, and there are many times when you want to change menus dynamically in response to program conditions.

The following topics describe how to associate code with menu events. For specific code examples that demonstrate ways to dynamically modify menus in a running application, refer to "Creating an MDI application."


Menu component events

Section topics	See also

The MainMenu component is different from most other components in that it doesn't have any associated events. You access the events for items in a main menu by means of the Menu Designer.

By contrast, the PopupMenu component has one event: the OnPopup event. This is necessary because pop-up menus have no menu bar, therefore no OnClick event is available prior to users opening the menu.

Handling menu item events

There is only one event for menu items (as opposed to menu components): The OnClick event. Code that you associate with a menu item's OnClick event is executed whenever the user chooses the menu item in the running application, either by clicking the menu command or by using its accelerator or shortcut keys.

To generate an event handler for any menu item:

1.	From the Menu Designer window, double-click the menu item.

2.	Inside the begin..end block, type the code you want to execute when the user clicks this menu command.

You can also easily generate event handlers for menu items displayed in a form. This procedure does not apply to pop-up menu items, as they are not displayed in the form at design time.

To generate an event handler for a menu item displayed in the form, simply click the menu item (not the menu component). For example, if your form contains a File menu with an Open menu item below it, you can click the Open menu item to generate or open the associated event handler.

This procedure applies only to menu items in a list, not those on a menu bar. Clicking a menu item on a menu bar opens that menu, displaying the subordinate menu items.

Associating a menu item with an existing event handler

You can associate a menu item with an existing event handler, so that you don't have to rewrite the same code in order to reuse it.

To associate a menu item with an existing OnClick event handler:

1.	In the Menu Designer window, select the menu item.

2.	On the Properties page of the Object Inspector, ensure that a value is assigned to the menu item's Name property.

3.	On the Events page of the Object Inspector, click the down arrow next to OnClick to open a list of previously written event handlers.

Only those event handlers written for OnClick events in this form appear in the list.

4.	Select from the list by clicking an event handler name.

The code written for this event handler is now associated with the selected menu item.


See also

Working with the Code editor

Blocks within event handlers

Controlling the flow of code execution


Working with the Code editor

Section topics	See also

Using the Delphi Code editor, you can easily view and modify your source code.

When you open a new project, Delphi automatically generates a page in the Delphi Code editor for the initial form unit file. When you add a new form, unit, or other file to the project, Delphi adds a new Code editor page to contain the associated source code or other data. If you open a file that's not part of the current project, it also appears on its own page . In addition, the Code editor lets you open any text file for viewing or modification.

The Delphi Code editor is a full-featured, customizable ASCII text editor that includes:

Syntax highlighting

Multiple and group undo

Four predefined editor settings: Default, IDE Classic, BRIEF, and Epsilon

Context-sensitive help for language elements

These topics introduce the major features of the Code editor. It describes how to navigate among multiple Code editor files and how to switch between your source code and the form design window. In addition, it shows how to:

View components as code

Search for text, objects, symbols

Customize the Code editor


Viewing files in the Code editor

Section topics	See also

The Code editor represents each open file with a tab at the top of the editor window--you can easily switch between open files in the editor by clicking the appropriate file tab. When you click a tab, the editor displays the file on an editor page.

You can use any of the following methods to view a specific file in the Code editor:

Choose File|Open to open an existing file.

For already open files, click the tab for the file you want to view.

Press Ctrl+Tab to move forward through the currently open files in the editor, and press Shift+Ctrl+Tab to move backward.

Choose View|Units to open the View Unit dialog box, and choose the unit you want to view.

Use SpeedBar buttons to toggle between the current form and its associated unit, or to open the View Unit dialog box.

Place the cursor on a file name within the editor, right-click and choose Open File At Cursor.

Choose View|Project Manager and double click on a unit in the project window.

To close all open files in the project, close the Code editor by double-clicking the Control-menu box of the edit window. To close a single page, choose Close Page from the Code editor Context menu.


Viewing the associated form

Section topics	See also

When viewing a file in the Code editor, you can return to the form associated with the current file or to a different form at any time.

To return to the form, choose from the following methods:

Click any part of the form that is visible under the Code editor.

Choose View|Form to open the View Form dialog box, and choose the form you want to view.

Use Speedbar buttons to display the current form, or to open the View Form dialog box.

Press F12 (it's a toggle).

Note:	If you view a form that is different from the one associated with the currently displayed code in the editor, the editor changes code pages to keep in sync with the selected form. Because of this, when you return to the Code editor, it might not display the code you were previously viewing.


Viewing components as code in the editor

Section topics	See also

When you add a component to a form, Delphi generates an instance variable for the component and adds it to the form's type declaration. Similarly, when you delete a component, Delphi removes the corresponding type declaration from the source code. You can view similar code being added or removed from the Code editor.

To view code as Delphi generates it:

1.	Move the form so you can view both the form and the Edit window (click the form's Title bar and drag it to a location that lets you see the entire Code editor).

2.	Scroll in the Code editor until the form's type declaration section is visible.

3.	Add a component to the form while watching what happens in the Code editor.

4.	Delete the component, again while viewing the Code editor.

Note:	Delphi does not remove event handlers (or methods) associated with components you delete because those event handlers might be called by other components in the form. You can still run your program so long as the method declaration and the method itself both remain in the unit file. If you delete the method without deleting its declaration, Delphi generates an error message.

Note:	Do not edit any code that Delphi generates. Edit only code that you create.


Code editor Context menu

Section topics	See also

Right-click in the Code editor to access its Context menu, which contains many commonly used edit commands, debugging commands, and commands that let you navigate throughout the Delphi development environment. For information on the commands contained on the Context menu, select a menu item, and press F1.

Code editor context menu:


Searching in the Code editor

Section topics	See also

Use the Search menu to locate specific text, objects, units, variables, and symbols:

Find searches for specific text. If you want to search in more than the current file, use the Find In Files page, where you can specify whether to search in all open files, all files in the project, or named directories.

Find In Files opens only the Find In Files page of the Find Text dialog box.

Replace searches for specific text and replaces it with new text.

Search Again repeats a search.

Incremental Search searches for text as you type.

Go To Line Number moves the cursor to a specific line number.

To learn more about any of the commands on the Search menu, select the command and press F1.


Customizing the editor

Section topics	See also

To customize the Delphi Code editor, choose Tools|Environment Options.

In the Environment Options dialog box, you can choose one of four predefined editor settings. If you want, you can then customize the behaviors and appearance of the editor by:

Selecting a default editor and customizing its keymapping

Choosing color settings for the editor

Specifying display and file options

Choosing code-writing assistance options


Selecting a default editor

Section topics	See also

On the Editor page, use the Editor SpeedSetting drop-down list to select the default keymappings for your editor. These options are described in the following table.

Option	Automatically sets
Default Keymapping	Auto Indent Mode, Insert Mode, Smart Tab, Backspace Unindents, Group Undo, Overwrite Blocks, Use Syntax Highlight
IDE Classic	Auto Indent Mode, Insert Mode, Smart Tab, Backspace Unindents, Cursor Through Tabs, Group Undo, Persistent Blocks, Use Syntax Highlight
BRIEF Emulation	Auto Indent Mode, Insert Mode, Smart Tab, Backspace Unindents, Cursor Through Tabs, Cursor Beyond EOF, Keep Trailing Blanks, BRIEF Regular Expressions, Force Cut And Copy Enabled, Use Syntax Highlight
Epsilon Emulation	Auto Indent Mode, Insert Mode, Smart Tab, Backspace Unindents, Cursor Through Tabs, Group Undo, Overwrite Blocks, Use Syntax Highlight

Once you choose a default keymapping, you can customize the behavior of the Code editor using the other options on the Editor page. To learn more about the options on this page, press F1 or click the Help button in the dialog box.


Choosing color settings for the editor

Section topics	See also

Use the Colors page in the Environment Options dialog box to specify colors for the different elements of your code. The sample box at the bottom of the Colors page shows how your settings will look.

The Color SpeedSetting configures the Code editor display according to predefined color combinations. After picking a predefined set of colors, you can further customize the display of your code; you can specify foreground and background colors and text attributes for anything listed in the Element list box.


Specifying display and file options

Section topics	See also

Use the Display page in the Environment Options dialog box to customize the display and font options for the Code editor. The sample window displays the selected font. The new settings take effect when you click OK.

You can choose to have the editor open full-screen, use BRIEF cursor shapes, and create backup files. You can also specify whether you want the gutter and right margin to be visible and how wide you want them to be (among other things, the gutter shows breakpoints in the integrated debugger). Click the Help button in the dialog box to learn more about the available options.


What kind of help do you want while coding?

Section topics	See also

You can choose to have context-sensitive pop-up windows help you with your code writing. Do this on the Code Insight page of the Environment Options dialog box. You have these choices:

Option	When enabled
Code Completion	When you type the name of a class followed by a period, a dialog box pops up displaying a list of properties, methods, and events appropriate to the class. You can then select the item to be entered in your code. When you enter an assignment statement and press Ctrl+Spacebar, the list of arguments that are valid for the variable is displayed. You can select the argument you want entered in your code.
Code Parameters	When you type a method call followed by the open parenthesis, a list box pops up, and you can view the syntax for the arguments to the method.
Code Templates	Common programming statements can be inserted into your code automatically when you press Ctrl+J to display a list of code templates. You can edit and add to the list.
Tool Tip Expression Evaluation	When you are compiling with Debug Information checked in the Project|Options dialog box and the compile process is stopped, you can point to a variable to display the value of that variable. It's best to uncheck Optimization on the Compiler page of the Project|Options dialog box when using this feature. Otherwise the feature might not always give you the value of a local variable.
For more information, click the Help button on the Code Insight page.


Using packages

Section topics	See also

Delphi packages are specially compiled dynamic-link libraries. Runtime packages allow code-sharing among applications, reducing executable size and conserving system resources. Design-time packages facilitate manipulation of components in the IDE and are used to create special property editors for custom components. A single package can function at both design time and runtime, and design-time packages frequently work by calling runtime packages.

The Delphi Visual Component Library (VCL), itself, is split up into packages, containing code that can be shared among applications. For example, most of Delphi's commonly used components reside in a package called VCL30.DPL (for Delphi package library). Each time you create an application, you can specify that it uses VCL30.DPL. When you compile an application created this way, the application's executable image contains only the code and data unique to it; the common code is in VCL30.DPL. A computer with several package-enabled applications installed on it needs only a single copy of VCL30.DPL, which is shared by all the applications and the Delphi integrated development environment (IDE) itself.

If you choose to, you can build your Delphi applications with a selection of VCL packages and custom packages that you or a third-party developer created.

These topics explain how to:

Use packages in your applications

Create and edit packages

Compile packages

Deploy and distribute packages


Why use packages?

Section topics	See also

Using runtime packages is optional, but it offers several advantages over conventional programming. By compiling reused code into a runtime package, you can share it among applications. For example, all of your applications--including the Delphi IDE itself--can access standard components through packages. The result is a dramatic savings in both system resources and hard disk storage. Moreover, packages allow faster compilation because only code unique to the application is compiled with each build.

Design time packages must be used if you want to add custom components to the IDE.


Using runtime packages

Section topics	See also

You can deploy runtime packages with your Delphi applications. The packages can be those that shipped with Delphi and your own custom packages. (See "Creating a package.")

To run an application that uses packages, a computer must have both the application's .EXE file and all the packages (.DPL files) that the application uses. When you deploy an application, make sure that users have correct versions of any required DPLs.

To use runtime packages in an application:

1.	Load or create a project in the IDE.

2.	Choose Project|Options.

3.	Choose the Packages tab.

4.	Select the Build With Runtime Packages check box, and enter one or more package names in the edit box. (Runtime packages associated with installed design-time packages are already listed in the edit box.) To add a package to an existing list, click the Add button next to the edit box and enter the name of the new package in the Add Package dialog box. Or to browse from a list of available packages, click the Browse button next to the Package Name edit box.

Note:	If you change the Search Path in the Add Package dialog box, you will be changing the global Library Path for Delphi.

You do not need to include file extensions with package names. If you type directly into the Runtime Packages edit box, separate multiple names with semicolons. For example,

VCL30;VCLDB30;VCLDBX30

Packages listed in the Runtime Packages edit box are automatically linked into your application when you compile. Duplicate package names are ignored, and if the edit box is empty the application is compiled without packages. Runtime packages are selected for the current project only. To make the current choices into automatic defaults for new projects, select the Default check box at the bottom of the dialog box.

Note:	When you create an application with packages, you still need to include the names of the original Delphi units in the uses clause of your source files. For example, the source file for your main form might begin like this:

unit MainForm;

interface

uses

 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

Each of the units referenced in this example is contained in the VCL30 package. Nonetheless, you must keep these references in the uses clause, even if you use VCL30 in your application, or you will get compiler errors.


Deciding which runtime packages to use

Section topics	See also

Delphi ships with several precompiled runtime packages, which supply basic Object Pascal and VCL support. In previous versions of Delphi, the functionality provided by these packages was obtained by binding a complete copy of the component library into each application.

The VCL30 package contains the most commonly used components, system functions, and Windows interface elements. It does not include database or Windows 3.1 components, which are available in separate packages. The following table lists the runtime packages shipped with Delphi and the units they contain.

Package	Units
VCL30.DPL	Ax, Buttons, Classes, Clipbrd, Comctrls, Commctrl, Commdlg, Comobj, Comstrs, Consts, Controls, Ddeml, Dialogs, Dlgs, Dsgnintf, Dsgnwnds, Editintf, Exptintf, Extctrls, Extdlgs, Fileintf, Forms, Graphics, Grids, Imm, IniFiles, Isapi, Isapi2, Istreams, Libhelp, Libintf, Lzexpand, Mapi, Mask, Math, Menu, Messages, Mmsystem, Nsapi, Ole2I, Oleconst, Olectnrs, Olectrls, Oledlg, Penwin, Printers, Proxies, Registry, Regstr, Richedit, Shellapi, Shlobj, Stdctrls, Stdvcl, Sysutils, Tlhelp32, Toolintf, Toolwin, Typinfo, Vclcom, Virtintf, Windows, Wininet, Winsock, Winspool, Winsvc
VCLX30.DPL	Checklst, Colorgrd, Ddeman, Filectrl, Mplayer, Outline, Tabnotbk, Tabs
VCLDB30.DPL	Bde, Bdeconst, Bdeprov, Db, Dbcgrids, Dbclient, Dbcommon, Dbconsts, Dbctrls, Dbgrids, Dbinpreq, Dblogdlg, Dbpwdlg, Dbtables, Dsintf, Provider, SMintf
VCLDBX30.DPL	Dblookup, Report
DSS30.DPL	Mxarrays, Mxbutton, Mxcommon, Mxconsts, Mxdb, Mxdcube, Mxdssqry, Mxgraph, Mxgrid, Mxpivsrc, Mxqedcom, Mxqparse, Mxqryedt, Mxstore, Mxtables, Mxqvb
INET30.DPL	Cgiapp, Copyprsr, Httpapp, Isapiapp, Masks, Scktcomp, Syncobjs, Webconst
INETDB30.DPL	Dbweb
QRPT30.DPL	Qr2const, Qrabout, Qralias, Qrctrls, Qrdatasu, Qrexpbld, Qrextra, Qrprev, Qrprgres, Qrprntr, Qrqred32, Quickrpt
TEE30.DPL	Arrowcha, Bubblech, Chart, Ganttch, Series, Teeconst, TeeFunci, TeEngine, Teeprocs, Teeshape
TEEDB30.DPL	Dbchart, Qrtee
IBEVNT30.DPL	Ibctrls, Ibevnts, Ibproc32, Ibreg

Suppose you want to create a client/server database application that uses packages. In this case, you need at least two runtime packages: VCL30 and VCLDB30. If you want to use Check list components in your application, then you also need VCLX30.

Note:	In this example, you don't have to include VCL30 in the list you type in the Project Options dialog box, because VCL30 is referenced in the requires clause of VCLDB30. (See "The requires clause.") Your application will compile just the same whether or not VCL30 is included in the list.


Using design-time packages

Section topics	See also

In Delphi all components are installed in the IDE as packages. Delphi also uses packages to create special property editors for custom components.

Delphi ships with the following design-time component packages preinstalled in the IDE.

Package	Component palette pages
DCLSTD30.DPL	Standard, Additional, System, Win32, Dialogs
DCLTEE30.DPL	Additional (TChart component)
DCLDB30.DPL	Data Access, Data Controls
DCLMID30.DPL	Data Access (TMIDASConnection component)
DCL31W30.DPL	Win 3.1
DCLNET30.DPL DCLISP30.DPL	Internet
DCLSMP30.DPL	Samples
DCLOCX30.DPL	ActiveX
DCLQRT30.DPL	QReport
DCLDSS30.DPL	Decision Cube
IBEVNT30.DPL	Samples (IBEventAlerter component)

These design-time packages work by calling runtime packages, which they reference in their requires clauses. (See "The requires clause.") For example, DCLSTDLIB references VCL30. DCLSTDLIB itself contains additional functionality that makes most of the standard components available on the Component palette.

To choose the design-time packages you want to use in your project:

1.	Choose Project|Options, and select the Packages tab.

2.	In the Design Packages list box, check the packages you want to use, and uncheck the ones you don't need.

3.	To add a package to the list, click the Add button beneath the list box.

4.	To make your selections the default for future projects as well, check the Default check box in the bottom corner of the dialog box.

5.	Click OK.


Installing your own components

Section topics	See also

If you want additional components available to you from the Component palette, you can install your own component packages or component packages from third-party developers. See "Adding components to Delphi" for instructions.


Creating and editing packages

Section topics	See also

A package source file does not contain type, data, procedure, or function declarations. Instead it contains:

A name for the package.

A list of other packages to be required by, or linked to, the new package.

A list of unit files to be contained by, or bound into, the package when it is compiled. The package is essentially a wrapper for these source-code units, which contain the functionality of the compiled DPL.

Package source files, which end with the .DPK extension, are generated by the Package editor.


Creating a package

Section topics	See also

Refer to "Rules for packages" for more information about the steps outlined here.

To create a package:

1.	Load or create a project in the IDE.

2.	Choose File|New, select the Package icon, and click OK.

3.	In the New Package dialog box, enter a file name and (optionally) a description. You can include a directory path with the name; otherwise, the package will reside in the current directory. To open a file/directory selection dialog, click the Browse button. If you type a file name directly in the New Package dialog, the .DPK extension will be added automatically.

4.	Click OK to open the Package editor.

5.	Choose the Contains tab, then click the Add speed button.

6.	When the Add dialog box opens, you can add an existing unit file to the package's contains clause, create a new component that descends from an existing component, or import an ActiveX component.

To add a unit file to the package's contains clause, choose the Add Unit tab and type a file in the Unit File Name edit box. You can specify an entire directory path with the file name, or edit the search path in the Search Path edit box; if you edit the search path, you will be changing the global Library Path for Delphi. To add more unit files to the package, close the Add dialog by clicking OK, then click the Add speed button again in the Package editor.

To create a new component, choose the New Component tab. Enter the new component's class name, the name of its immediate ancestor, the name of the unit file where the component's declaration should be written, and the page on the Component palette where you want the new component displayed. You can specify an entire directory path with the file name, or edit the search path in the Search Path edit box; if you edit the search path, you will be changing the global Library Path for Delphi.

To import an ActiveX component, choose the Import ActiveX tab and select a registered component from the list box. To register a new component, click Add. To unregister a component, click Remove.

7.	In the Package editor, click the Requires tab to see a list of other packages in the requires clause of the package you are editing. VCL30 is listed by default. If you want to add more packages to the requires clause, click the Add speed button and proceed as in step 6 above.

8.	Click the Options speed button and click the Description tab.

Under Usage Options, check Design Package, Runtime Package, or both. Selecting only one of these options turns on the $RUNONLY or $DESIGNONLY compiler directive. (See "Inserting package-specific compiler directives in code.") To create a design-time package that is not installable in the IDE (but may still be called by other design-time packages), deselect both check boxes.

Under Build Control, select Rebuild As Needed or Explicit Rebuild. Explicit Rebuild turns on the -Z ($IMPLICITBUILD OFF) compiler switch, which prevents implicit recompilation of the package. (See "Using the command-line compiler.")

9.	Click OK to close the Package Options dialog and save your changes.

10.	In the Package editor, click the Compile speed button to compile your package.

Clicking the Compile speed button compiles your package into a DPL, but does not save the package source (.DPK) file. To save the .DPK file, right-click and select Save Package, or choose File|Save from Delphi's main menu bar.


Editing a package in the Package editor

Section topics	See also

There are several ways to open an existing package in the Package editor:

Choose File|Open (or File|Reopen) and select a .DPK file.

Choose Component|Install Packages, select a package from the Design Packages list, and click the Edit button.

Choose Component|Install Component and click the Into Existing Package tab. Then select a package and a unit file to add to the package's contains clause.

When the Package editor is open, click the Requires tab, select one of the packages in the requires list, right-click, and choose View Required Package.

You can also open a package by choosing File|Open and selecting a .DPL file. However, the Package editor always looks for a .DPK file to open, even if you select a .DPL. If it cannot find the .DPK file, the Package editor is sometimes able to open the .DPL on a read-only basis; in this case, you can resave the package as a .DPK for further editing.

The Contains page in the Package editor displays a list of unit files contained in the current package. You can add or remove files by clicking the appropriate speed button. To open a unit file in the Code editor, select the file, right-click, and choose View Unit.

The Requires page displays a list of other packages required by (linked to) the current package. You can add or remove files by clicking the appropriate speed button. To open one of the required packages in a new Package editor, select the package, right-click, and choose View Required Package.

The Package Options speed button lets you edit the package's description or set compiler and library options.


Editing package source files manually

Section topics	See also

Package source files, like project files, are generated by Delphi from information you supply. Like project files, they can also be edited manually. A package source file should be saved with the .DPK (Delphi package) extension to avoid confusion with other files containing Object Pascal source code.

To open a package source file in the Code editor:

1.	Open the package in the Package editor.

2.	Right-click in the Package editor and select View Package Source.

The package heading specifies the name for the package.

The requires clause lists other, external packages used by the current package. If a package does not reference other packages, then it does not need a requires clause.

The contains clause identifies the unit files to be compiled and bound into the package.

For example, the following code declares the VCLDB30 package.

package VCLDB30;

requires VCL30;

contains Db, Dbcgrids, Dbctrls, Dbgrids, Dbinpreq, Dblogdlg, Dbpwdlg, Dbtables;

end.


Rules for packages

Section topics	See also

Packages must follow these rules:

Package names

Package names must be unique within a project. If you name a package STATS, the Package editor generates a source file for it called STATS.DPK; the compiler generates an executable and a binary image called STATS.DPL and STATS.DCP, respectively. Use STATS to refer to the package in the requires clause of another package, or when using the package in an application.

The requires clause

The requires clause lists other, external packages that are used by the current package. It functions like the uses clause in a Delphi unit file. An external package listed in the requires clause is automatically linked at compile time into any application that uses both the current package and one of the units contained in the external package.

If the unit files contained in your package make references to other packaged units, you should include the other packages in your package's requires clause. If the other packages are omitted from the requires clause, the compiler will load the referenced units in the normal way (without DPL files).

Note:	Most packages that you create will require VCL30. Any package that depends on VCL units (including SysUtils) must list VCL30, or another package that requires VCL30, in its requires clause.

Avoid circular package references

Packages cannot contain circular references in their requires clauses. This means that

A package cannot reference itself in its own requires clause.

A chain of references must terminate without rereferencing any package in the chain. If package A requires package B, then package B cannot require package A; if package A requires package B and package B requires package C, then package C cannot require package A.

Avoid duplicate package references

Duplicate references in a package's requires clause--or in the Runtime Packages edit box--are ignored by the compiler. For programming clarity and readability, however, you should catch and remove duplicate package references.

The contains clause

The contains clause identifies the unit files to be bound into the package. If you are writing your own package, put your source code in .PAS files and list them in the contains clause. Do not include file-name extensions in the list; units are recompiled only if their .DCU files cannot be found or their .PAS files have changed since the last build.

Avoid redundant source code uses

A package cannot be listed in the contains clause of another package or the uses clause of a unit.

All units included directly in a package's contains clause, or indirectly in the uses clauses of those units, are bound into the package at compile time. The units contained (directly or indirectly) in a package cannot be contained in any other packages referenced in the requires clause of that package.

A unit cannot be contained (directly or indirectly) in more than one package used by the same application, including the Delphi IDE. This means that if you create a package that contains one of the units in VCL30, you won't be able to install your package in the IDE. To use an already-packaged unit file in another package, list the first package in the second package's requires clause.


Compiling packages

Section topics	See also

You can compile a package from the IDE or from the command line. When you rebuild any project that contains a package, the package is implicitly recompiled if necessary.

To recompile a package by itself from the IDE:

1.	Choose File|Open.

2.	Select Delphi Package Source (*.DPK) from the Files Of Type drop-down list.

3.	Select a .DPK file in the dialog.

4.	When the Package editor opens, click the Compile speed button.

You can insert compiler directives into your package source code. For more information, see "Inserting package-specific compiler directives in code," below.

If you compile from the command line, new package-specific switches are available. For more information, see "Using the command-line compiler."

Inserting package-specific compiler directives in code

The following table lists package-specific compiler directives that you can insert into your source code.

Directive	Purpose
{$IMPLICITBUILD OFF}	Prevents a package from being implicitly recompiled later. Use in .DPK files when compiling packages that provide low-level functionality, that change infrequently between builds, or whose source code will not be distributed.
{$G-} or {IMPORTEDDATA OFF}	Disables creation of imported data references. This directive increases memory-access efficiency, but prevents the unit where it occurs from referencing variables in other packages.
{$WEAKPACKAGEUNIT ON}	Packages unit "weakly." See "$WEAKPACKAGEUNIT directive."
{$DENYPACKAGEUNIT ON}	Prevents unit from being placed in a package.
{$DESIGNONLY ON}	Compiles the package for installation in the IDE. (Put in .DPK file.)
{$RUNONLY ON}	Compiles the package as runtime only. (Put in .DPK file.)
Note:	Including {$DENYPACKAGEUNIT ON} in your source code prevents the unit file from being packaged. Including {$G-} or {IMPORTEDDATA OFF} may prevent a package from being used in the same application with other packages. Packages compiled with the {$DESIGNONLY ON} directive should not ordinarily be used in applications, since they contain extra code required by the IDE. Other compiler directives may be included, if appropriate, in package source code. See "Compiler directives" for information on compiler directives not discussed here.

$WEAKPACKAGEUNIT directive

The $WEAKPACKAGEUNIT directive affects the way a .DCU file is stored in a package's .DCP and .DPL files. (For information about files generated by the compiler, see "Package files created by a successful compilation.") If {$WEAKPACKAGEUNIT ON} appears in a unit file, the compiler omits the unit from DPLs when possible, and creates a non-packaged local copy of the unit when it is required by another application or package. A unit compiled with this directive is said to be "weakly packaged."

For example, suppose you've created a package called PACK that contains only one unit, UNIT1. Suppose UNIT1 does not use any further units, but it makes calls to RARE.DLL. If you put {$WEAKPACKAGEUNIT ON} in UNIT1.PAS when you compile your package, UNIT1 will not be included in PACK.DPL; you will not have to distribute copies of RARE.DLL with PACK. However, UNIT1 will still be included in PACK.DCP. If UNIT1 is referenced by another package or application that uses PACK, it will be copied from PACK.DCP and compiled directly into the project.

Now suppose you add a second unit, UNIT2, to PACK. Suppose that UNIT2 uses UNIT1. This time, even if you compile PACK with {$WEAKPACKAGEUNIT ON} in UNIT1.PAS, the compiler will include UNIT1 in PACK.DPL. But other packages or applications that reference UNIT1 will use the (non-packaged) copy taken from PACK.DCP.

Note:	Unit files containing the {$WEAKPACKAGEUNIT ON} directive must not have global variables, initialization sections, or finalization sections.

The $WEAKPACKAGEUNIT directive is an advanced feature intended for developers who distribute their DPLs to other Delphi programmers. It can help you to avoid distribution of infrequently used DLLs, and to eliminate conflicts among packages that may depend on the same external library.

For example, Delphi's PenWin unit references PENWIN.DLL. Most projects don't use PenWin, and most computers don't have PENWIN.DLL installed on them. For this reason, the PenWin unit is weakly packaged in VCL30. When you compile a project that uses PenWin and the VCL30 package, PenWin is copied from VCL30.DCP and bound directly into your project; the resulting executable is statically linked to PENWIN.DLL.

If PenWin were not weakly packaged, two problems would arise. First, VCL30 itself would be statically linked to PENWIN.DLL, and so you could not load it on any computer that didn't have PENWIN.DLL installed. Second, if you tried to create a package that contained PenWin, a compiler error would result because the PenWin unit would be contained in both VCL30 and your package. Thus, without weak packaging, PenWin could not be included in standard distributions of VCL30.

Using the command-line compiler

When you compile from the command line, you can use the package-specific switches listed in the following table.

Switch	Purpose
-$G-	Disables creation of imported data references. Using this switch increases memory-access efficiency, but prevents packages compiled with it from referencing variables in other packages.
-LEpath	Specifies the directory where the package DPL file will be placed.
-LNpath	Specifies the directory where the package DCP file will be placed.
-Z	Prevents a package from being implicitly recompiled later. Use when compiling packages that provide low-level functionality, that change infrequently between builds, or whose source code will not be distributed.
Note:	Using the -$G- switch may prevent a package from being used in the same application with other packages. Other command-line options may be used, if appropriate, when compiling packages. See "The command-line compiler" for information on command-line options not discussed here.

Package files created by a successful compilation

To create a package, you compile a source file that has a .DPK extension. The base name of the .DPK file becomes the base name of the files generated by the compiler. For example, if you compile a package source file called TRAYPAK.DPK, the compiler creates a package called TRAYPAK.DPL.

The following table lists the files produced by the successful compilation of a package.

File extension	Contents
DCP	A binary image containing a package header and the concatenation of all DCU files in the package. A single DCP file is created for each package. The base name for the DCP is the base name of the DPK source file.
DCU	A binary image for a unit file contained in a package. One DCU is created, when necessary, for each unit file.
DPL	The runtime package. This file is a Windows DLL with special Delphi-specific features. The base name for the DPL is the base name of the DPK source file.


Distributing applications that use packages

Section topics	See also

When distributing an application that uses runtime packages, make sure that your users have the application's .EXE file as well as all the library (.DPL or .DLL) files that the application calls. If the library files are in a different directory from the .EXE file, they must be accessible through the user's Path. You may want to follow the convention of putting library files in the Windows\System directory. If you use InstallShield Express, your installation script can check the user's system for any packages it requires, instead of automatically reinstalling them.


Distributing packages to other developers

Section topics	See also

If you distribute runtime or design-time packages to other Delphi developers, be sure to supply both .DCP and .DPL files. You will probably want to include .DCU files as well.


Package collection files

Section topics	See also

Package collections (.DPC files) offer a convenient way to distribute packages to other developers. Each package collection contains one or more packages, including DPLs and any additional files you want to distribute with them. When a package collection is selected for IDE installation, its constituent files are automatically extracted from their .DPC container; the Installation dialog box offers a choice of installing all packages in the collection or installing packages selectively.

To create a package collection:

1.	Choose Tools|Package Collection Editor to open the Package Collection editor.

2.	Click the Add Package speed button, then select a DPL in the Select Package dialog and click Open. To add more DPLs to the collection, click the Add Package speed button again. A tree diagram on the left side of the Package editor displays the DPLs as you add them. To remove a package, select it and click the Remove Package speed button.

3.	Select the Collection node at the top of the tree diagram. On the right side of the Package Collection editor, two fields will appear:

In the Author/Vendor Name edit box, you can enter optional information about your package collection that will appear in the Installation dialog when users install packages.

Under Directory List, list the default directories where you want the files in your package collection to be installed. Use the Add, Edit, and Delete buttons to edit this list. For example, suppose you want all source code files to be copied to the same directory. In this case, you might enter Source as a Directory Name with C:\MyPackage\Source as the Suggested Path. The Installation dialog box will display C:\MyPackage\Source as the suggested path for the directory.

4.	In addition to DPLs, your package collection can contain .DCP, .DCU, and .PAS (unit) files, documentation, and any other files you want to include with the distribution. Ancillary files are placed in file groups associated with specific packages (DPLs); the files in a group are installed only when their associated DPL is installed. To place ancillary files in your package collection, select a DPL in the tree diagram and click the Add File Group speed button; type a name for the file group. Add more file groups, if desired, in the same way. When you select a file group, new fields will appear on the right in the Package Collection editor:

In the Install Directory list box, select the directory where you want files in this group to be installed. The drop-down list includes the directories you entered under Directory List in step 3, above.

Check the Optional Group check box if you want installation of the files in this group to be optional.

Under Include Files, list the files you want to include in this group. Use the Add, Delete, and Auto buttons to edit the list. The Auto button allows you to select all files with specified extensions that are listed in the contains clause of the package; the Package Collection editor uses Delphi's global Labrary Path to search for these files.

5.	You can select installation directories for the packages listed in the requires clause of any package in your collection. When you select a DPL in the tree diagram, four new fields appear on the right side of the Package Collection editor:

In the Required Executables list box, select the directory where you want the .DPL files for packages listed in the requires clause to be installed. (The drop-down list includes the directories you entered under Directory List in step 3, above.) The Package Collection Editor searches for these files using Delphi's global Library Path and lists them under Required Executable Files.

In the Required Libraries list box, select the directory where you want the .DCP files for packages listed in the requires clause to be installed. (The drop-down list includes the directories you entered under Directory List in step 3, above.) The Package Collection Editor searches for these files using Delphi's global Library Path and lists them under Required Library Files.

6.	To save your package collection source file, choose File|Save. Package collection source files should be saved with the .PCE extension.

7.	To build your package collection, click the Compile speed button. The Package Collection editor generates a .DPC file with the same name as your source (.PCE) file. If you have not yet saved the source file, the editor queries you for a file name before compiling.

To edit or recompile an existing .PCE file, select File|Open in the Package Collection editor.


See also

The command-line compiler

Compiler directives


Writing Object Pascal code

Section topics	See also

These topics explain the primary features of the Object Pascal language and shows you how to use it to write code within event handlers and other parts of your application. After reading this and "Programming with Delphi objects," you should be able to develop useful and more sophisticated Delphi applications.

These topics assume you already have some programming experience. You certainly don't have to be an expert, however. If you've programmed successfully in any of the popular programming languages, you'll recognize common concepts in Object Pascal. While the topics do show you the most common uses of Object Pascal in a Delphi application, it doesn't explain all there is to know about the language.

You can find in-depth information about Object Pascal in Delphi's online Help--look up the Language Definition topic. The information there includes syntax diagrams of all Object Pascal language constructs. You might also want to read one or more of the excellent books published about Pascal, especially those that discuss Delphi, Turbo Pascal, and Borland Pascal.

If you are familiar with the Borland Pascal language already, you can probably either skim these topics rapidly, or skip it altogether and go directly to "Programming with Delphi objects." You'll still want to refer to the Language Definition in Delphi's online Help to learn about the new additions to the language, however. (Use the Index page of the Help dialog box.)

The subjects of these topics are:

Writing readable code

Writing assignment statements

Declaring identifiers

Declaring constants

Calling procedures and functions

Controlling the flow of code execution

Object Pascal blocks

Understanding scope

Writing a procedure or function

Defining new data types

Understanding Object Pascal units

To use these topics effectively, you need to understand how a Delphi project is structured and how to create event handlers. If you are uncertain about these topics, see Part II.


Writing readable code

Section topics	See also

Although you write code for the compiler to compile and turn into an executable program, you want yourself and others to be able to read your code easily.


Coding style

Section topics	See also

As you prepare for writing your first code statements, you should realize that it doesn't matter to the compiler how you space, indent, or capitalize your Object Pascal code, or how you break lines of code to continue on the next line. As long as the code is syntactically correct, the compiler can handle it properly. For example, this code compiles just fine:

PROCEDURE

 TFORM1.

Button1CLICK(SENDER        :

      TOBJECT);begin

 Edit1.Color

:=

CLRED;END;

You would probably find this version of the event handler easier to read, however:

procedure TForm1.Button1Click(Sender: TObject);

begin

 Edit1.Color := clRed;

end;

Choose a code indentation and capitalization style, and break lines of code where you need to, so that your code is easy for you and others to read.


Commenting your code

Section topics	See also

You can also place comments in your code anywhere you like.

To put comments in your code,

Enclose all the text of your comments in braces ({ }).

For example, you could add the following comment to the previous event handler:

procedure TForm1.Button1Click(Sender: TObject);	{ Changes the color of the edit box }

begin

 Edit1.Color := clRed;

end;

Comments can extend over many lines. The compiler considers everything after the first brace ({) to be a comment until it finds the closing brace (}):

{ This comment runs on a bit

and really doesn't say anything

useful, but it shows you that comments

can have multiple lines of text }

procedure TForm1.Button1Click(Sender: TObject);	{ Changes the color of the edit box }

begin

 Edit1.Color := clRed;

end;

Don't forget to include the closing brace, or the compiler considers the rest of your code to be a comment.

If you want to quickly comment a line of code or add a comment to the end of a line, you can use a double-slash (//). The compiler treats everything from the double-slash to the end of the line as a comment:

procedure TForm1.Button1Click(Sender: TObject);	// Changes the color of the edit box

begin

 Edit1.Color := clRed;

end;

The compiler ignores all comments in the code, treating them as if they were spaces. Look up the Comments topic in Delphi Help for more information about adding comments to your code.

Use comments to help the reader of your code (including yourself) understand its logic. Even though you understand how your code works when you write it, it might not seem so clear to you when you examine it later.


Writing assignment statements

Section topics	See also

The most common task you'll encounter when writing code within event handlers is assigning a new value to a property or variable. You can use the Object Inspector to change the value of a property while designing the user interface, but also you'll often want to change the value of a property while the application is running. Some properties can be changed only at runtime; such properties are noted as run-time only in the property reference topics of online Help. To change a property or variable at runtime, you need to write an assignment statement.

Assignment statements consist of two parts, separated by the Object Pascal assignment operator (:=). The left side of an assignment statement is a property or variable name, while the right side of the statement is the new value for the property or variable. The assignment statement, like all Object Pascal statements, ends with a semicolon (;).

Here is a simple assignment statement within an OnClick event handler for a button control. This statement assigns the value clRed to the Color property of an edit box control named Edit1:

procedure TForm1.Button1Click(Sender: TObject);

begin

 Edit1.Color := clRed;

end;

Assuming that an edit box control named Edit1 and a button control named Button1 exist on the form, the assignment statement runs and the edit box turns red when the user clicks the button.

Note that the component name precedes the property name, with a period (.) separating the component and property. If you include the component name with the property, there is no confusion as to which Color property should be changed. In fact, if you omit the component name, the color of the form changes when the user clicks the button. For more information about this topic, see "Understanding scope."


Assigning values to properties and variables

Section topics	See also

Whether you assign a value to a property or to a variable, you follow the same rules. The value on the right side of the assignment statement is assigned to the property or variable on the left side of the assignment statement. When a value is assigned, a copy of the value is placed into the property or variable. The value being assigned can be a constant, a literal value, a variable, or another property value. For example,

var

 Name: string;

 Number: Integer;

begin

 Name := Edit1.Text;	{ Assigns a property value to a variable }

 Number := 15;	{ Assigns a literal value to a variable }

 Edit2.Text := 'Welcome to Delphi, ' + Name;	{ Assigns a string made up of a string
constant and a variable string to a property }

end;

The following code example shows you how to use an assignment statement to set or change the color of a component--in this case, an edit box--in response to a button click. Suppose, instead, that you want the user to be able to use a color grid to change the color of an edit box on a form. You want the edit box to change color when the user clicks a color rectangle in the color grid.

For example, the following form has this event handler attached to the OnClick event of the color grid:

procedure TForm1.ColorGrid1Click(Sender: TObject);

begin

 Edit1.Color := ColorGrid1.ForegroundColor;

end;

Example: assigning a property value to another property

When you run the application and click a color on the color grid, the assignment statement assigns the value of the ColorGrid1.Foreground property to the Edit1.Color property. In other words, the color of the edit box control becomes the color the user clicks in the color grid.

You can assign the value of a property, variable, constant, or literal to a variable or property if the assigned value is of the same type or is assignment-compatible with the variable or property receiving the new value. (To read about type and assignment compatibility, see "Type and assignment compatibility.")

The type of a property or variable defines the set of values the property or variable can have and the operations your code can perform on it. In the previous example, both Color (a property of the edit box) and ForegroundColor ( a property of the color grid) are of type TColor.

You can find out the type of a property by looking up the property in online Help, or by selecting the property in the Object Inspector and pressing F1. The type is listed at the end of a property declaration. For example, the Color property declaration declares Color to be of type TColor.

property Color: TColor;

Note:	You can't assign new values to all properties. Some properties are read-only, which means you can find out what the value of the property is, but you can't change its value in your application. Properties that are read-only are noted as such in Delphi's online Help.


Declaring identifiers

Section topics	See also

Identifiers are names you give to any part of a Delphi application, including variables, types, procedures, functions, methods, and so on. (All identifiers are italicized in the Delphi books, except in the code examples.) Object Pascal requires that you declare identifiers before using them in your code.

If you haven't used a compiled language before, you might wonder why it is necessary to declare identifiers before you use them. As with all compiled languages, the compiler must be given explicit information about the identifiers before the compiler can create executable code that uses them.

The compiler can then check your code to ensure that the values you assign to variables or properties are of the correct type. If they aren't, Delphi displays a syntax error message, so you can correct the problem. Languages that aren't compiled can't protect you from assigning a value of a wrong type, a common programming mistake. Object Pascal is known as a strongly typed language.

Also, because Object Pascal is a compiled language, your Delphi applications will run much faster than applications you build with an interpreted language. Declaring identifiers before you use them reduces the number of programming errors and increases the efficiency of your code.


Declaring variables

Section topics	See also

A variable is a name in code that stands for a memory address, the contents of which can change as the code runs. You need to declare a variable before using it. There are two steps in declaring a variable:

1.	Naming a variable

2.	Giving a variable a type

Naming a variable

When you declare a variable, you need to select a name for it. You should try to select a name that reminds you of the variable's purpose, so your code is easier to read. For example, Day, Name, and Total are more memorable names than X, Y, and Z.

Beyond choosing meaningful names for your variables, you should remember a few rules that apply to variables and all other identifiers.

Identifiers can be up to 63 characters in length.

If your identifiers are longer than 63 characters, the compiler ignores the extra characters.

Identifiers must begin with either a letter or an underscore (_) character.

Subsequent characters can be letters, underscores, or the digits 0-9.

Identifiers can't contain any other symbols, such as $, %, *, and so on.

For example, identifiers called Do@Noon or Gross%Return result in a syntax error.

You cannot use an Object Pascal reserved word for naming an identifier.

Reserved words have special meaning to the compiler. If you use one of these reserved words to name an identifier, Delphi displays an error message. You can find a list of Object Pascal reserved words in online Help under the Reserved Words topic. All reserved words are boldfaced in the Delphi documentation.

Avoid using identifiers already defined in the Object Pascal language to name your identifiers.

For example, the words Boolean and Integer are predefined data types (see "Data types" to read about predefined data types). You can use either of these words to name a variable without generating an error message, but if you do, you won't be able to use the Boolean and Integer types in your code.

Giving a variable a type

When declaring a variable, you must state its type. The type of a variable defines the set of values the variable can have.

In general, you declare a variable where you want to use it. For example, if you want to use a variable in an event handler, you declare the variable within the event handler. For more details about where to declare variables, read "Object Pascal blocks" and "Understanding scope."

Precede all variable declarations with the reserved word var. The declaration itself is made up of two parts: the left side is the name of the new variable, and the right side declares what the variable's type is. The two parts are separated by a colon (:). This example declares three variables, Value, Sum, and Line:

var

 Value: Integer;

 Sum: Integer;

 Line: string;

Because Value and Sum are both of type Integer, the variable declaration could look like this, with a comma separating the two variables of the same type:

var

 Value, Sum: Integer;

 Line: string;

The following example assumes that there is an edit box named Edit1 and a button named AddButton on the form. Three variables (X, Y, and Sum) are declared as type Integer. When the user clicks the AddButton, two variables, X and Y, are assigned Integer values and added together. The result, Sum, appears in the edit box:

procedure TForm1.AddButtonClick(Sender: TObject);

var

 X, Y, Sum: Integer;

begin

 X := 100;

 Y := 10;

 Sum := X + Y;

  Edit1.Text := IntToStr(Sum);

end;

This line assigns the value of an expression to a variable:

Sum := X + Y;

An expression is usually two identifiers related to one another through an operator. (To read about expressions, see the "Expressions" topic in online Help.) Because the Text property of an edit box is of type string and the Sum variable is of type Integer, this line is not permissible and causes a type mismatch error if you try to run or compile it:

Edit1.Text := Sum;

Instead, the last line of the event handler uses the IntToStr function from the Delphi run-time library to convert the Sum value to a text string, then assigns the value of the Sum variable to the Edit1.Text property.

This variation avoids the assigning of a value to Sum altogether:

procedure TForm1.AddButtonClick(Sender: TObject);

var

 X, Y: Integer;

begin

 X := 100;

 Y := 10;

 Edit1.Text := IntToStr(X + Y);

end;

Using functions in your code is discussed in more detail in "Calling functions," but it's useful to know that functions return a value of a particular type, and that value can then be assigned to a variable or property.

Data types

Object Pascal has several data types built into the language. You can create variables of any of these predefined types. The following table lists the predefined data types:

Category	Data type	Description
Integer	Shortint	Numbers without a fractional part, within the range of -128 to 127. Requires only one byte of memory.
	SmallInt	Numbers without a fractional part, within the range of -32768 to 32767. Requires two bytes of memory.
	Longint	Numbers without a fractional part, within the range of -2147483647 to 2147483647. Requires four bytes of memory.
	Integer	Varies, depending on the implementation. In this version of Object Pascal, Integer is the same as Longint.
	Byte	Numbers without a fractional part, within the range of 0 to 255. Requires one byte of memory.
	Word	Numbers without a fractional part, within the range of 0 to 65535. Requires two bytes of memory.
	Cardinal	Varies, depending on the implementation. In this version of Object Pascal, Cardinal is like Integer, but allows only non-negative values.
Real	Single	A number that can contain a fractional part with 7-8 significant digits. Requires 4 bytes of memory. (See "Real Types" in online Help for the range of each real type.)
	Double	A number that can contain a fractional part with 15-16 significant digits. Requires 8 bytes of memory.
	Extended	A number that can contain a fractional part with 19-20 significant digits. Requires 10 bytes of memory.
	Comp	A number that can contain a fractional part with 19-20 significant digits that requires 8 bytes of memory.
	Currency	A high-precision real number stored in an integer format, with four decimal places.
	Real	A number that can contain a fractional part with 11-12 significant digits. Requires 6 bytes of memory. Use Real only for compatibility with earlier Borland Pascal releases; otherwise use Double or Extended.
Boolean	Boolean	Can contain the values of True or False. Occupies one byte of memory.
Character	AnsiChar	An ANSI character.
	WideChar	A Unicode character.
	Char	Varies, depending on the implementation. In this version of Object Pascal, Char is the same as AnsiChar.
String	ShortString	A sequence of up to 255 characters.
	AnsiString	A dynamic-size string of virtually unlimited length.
	string	Varies, depending on the $H compiler directive. By default, string is the same as AnsiString.
Pointer	Pointer	An untyped pointer, that is, a pointer that doesn't point to any specific type.
PChar	PChar	A pointer to a null-terminated string.
Variant	Variant	A data type that can hold values of different types, and which can convert its value to other types as needed.

Besides the predefined data types, you can define your own data types. For many Delphi applications, the predefined data types are sufficient. These topics, however, have already discussed the use of a user-defined data type. The Color property is of type TColor, which isn't a predefined data type in Object Pascal, but a type defined by the creators of the Delphi run-time library. To learn more about predefined types, see "Type declarations." To read about defining your own data types, see "Defining new data types."

Type and assignment compatibility

The previous table lists five different data types that make up the integer category and five data types that make up the real category. Within each category, all types are compatible with each other. You can assign a value of one type to a variable or property of a different type but within the same category as long as the value falls within the range of possible values for the target variable or property.

For example, you can assign a variable of type Integer with the value 7 to a variable of type Shortint, because a Shortint type can contain any value from -127 to 128. If the value of the Integer variable is 300, however, you can't assign it to a Shortint variable, because 300 is outside the Shortint range. If you try, and have range-checking turned on (choose Options|Project and select Range Checking on the Compiler Options page), you'll generate a range error when you try to run the code containing the assignment. If you don't have range-checking turned on, the code runs, but the assigned value won't be what you expect.

In some cases, you can assign values to a variable or property that is within a different category. In general, this process works only when assigning values from a type of a smaller range to a type of a larger range of possible values. For example, you can assign a value of type Integer to a property of type Double, a type that accepts floating-point values, making the value now 10.0. You can't assign a Double value to a variable or property of type Integer, however, because integer values don't have fractional parts.

When you are unsure which types are compatible, you can refer to a complete set of rules for type and assignment compatibility. See the topics "Type compatibility" and "Assignment compatibility" in Delphi's online Help.


Declaring constants

Section topics	See also

While variables contain values that can change as your application runs, constants contain values that can't change. Constants are given a value at the time they are declared.

Just as variables are declared in a variable declaration, constants are declared in a constant declaration. A constant declaration begins with the reserved word const. This example declares three constants:

const

 Pi = 3.14159;

 Answer = 342;

 ProductName = 'Delphi';

Pi, Answer, and ProductName are all constant names that describe the value of the constant. The actual value of the constants, which can't change while the application runs, are 3.14159, 342, and 'Delphi', respectively. When naming constants, follow the same rules for naming variables and other identifiers given on "Declaring variables."

Like variables, constants also have types. Unlike variables, however, constants assume the type of the value declared in the constant declaration part. For example, the constant Pi is of type Real because the value 3.14159 is real number, Answer is of type Integer because 342 is an integer, and ProductName is of type string, because 'Delphi' is a text string.

Note that the equal sign (=) in a constant declaration is different from the assignment operator (:=) in assignment statements. The equal sign in a constant declaration states that the left side and the right side of the declaration are equal values.

It's a common mistake to misuse an equal sign operator in Object Pascal code. When the compiler finds an equal sign rather than an assignment operator, it expects that the left side of the statement is a constant or that two values are being compared. If that isn't how you are using the equal sign, Delphi displays a syntax error message.

If you make the reverse mistake of using an assignment operator in a constant declaration, a syntax error occurs, and a message appears telling you that the compiler expected an equal sign.


Calling procedures and functions

Section topics	See also

Procedures and functions are modular pieces of code that perform some specific task. They are known as subroutines in other languages. The Delphi run-time library contains:

Procedures and functions in Delphi's System, SysUtils, and Math units

Variables and constants used with these routines and their data types

Exception objects used to report errors

You don't need to write the code--the procedures and functions are already written. While you don't need to understand the logic of the code within the procedures and functions, you need to understand what they do.

When procedures and functions are declared within objects, which include components and controls, they are called methods. An object is a data type you can read in "Programming with Delphi objects."


Calling procedures

Section topics	See also

Perhaps you've noticed that all event handlers begin with the reserved word procedure. All event handlers are procedures, subroutines that divide the logic of your application into manageable chunks. Each event handler contains just the code you want executed when the event occurs.

To use an existing procedure,
Type the name of the procedure as a statement in your code.

This is called "calling a procedure."

For example, the memo control has a CutToClipboard method that removes the text the user has selected within the memo control and stores it in the Clipboard object. Because this functionality is built into the method, you need to know only what the method does and how to use it. This is how you call the CutToClipboard method of a memo control named Memo1:

Memo1.CutToClipboard;

Note that the name of the control, Memo1, is part of the method call. By including the name of the control, you are specifying which CutToClipboard method you want to call. Separate the name of the control from the method call with a period (.). If you omit the name of the control, component, or object in a method call, Delphi usually displays this syntax error:

Unknown identifier

When the application calls Memo1.CutToClipboard, the code within the CutToClipboard method runs, and the user's selected text is cut to the Clipboard object.

Cut, Copy, Paste, and Clear All: An example

Here's a sample form that calls procedures in the Delphi library:

Each button has an OnClick event handler, as follows:

procedure TForm1.CutClick(Sender: TObject);

begin

 Memo1.CutToClipboard;

end;

procedure TForm1.CopyClick(Sender: TObject);

begin

 Memo1.CopyToClipboard;

end;

procedure TForm1.PasteClick(Sender: TObject);

begin

 Memo1.PasteFromClipboard;

end;

procedure TForm1.ClearAllClick(Sender: TObject);

begin

 Memo1.Clear;

end;

When you run the application, you can type text into the memo control and cut, copy, paste, and clear text as you want. When you click the Cut button, the memo's CutToClipboard method runs. When you click the Copy button, the memo's CopyToClipboard method runs, and so on.

Calling procedures with parameters

Many procedures require you to specify parameters. The statements of the called procedure use the values passed in the parameters when the procedure's code runs; the values are treated as variables declared within the procedure. Parameters are surrounded with parentheses in a procedure call. For example, the LoadFromFile method is declared in a TStrings object like this:

procedure LoadFromFile(const FileName: string);

FileName, the sole parameter of the method, is of type string. When you call the LoadFromFile method, you specify which file you want loaded by specifying a string as the FileName parameter. This code would read the MYFILE.TXT file into the lines of a memo control:

Memo1.Lines.LoadFromFile('MYFILE.TXT');

Instead of directly specifying a string value, you could use a variable or expression of type string:

var

 Name: string;

begin

 Name := 'MYFILE.TXT';

 Memo1.Lines.LoadFromFile(Name);

end;

Finally, you could specify a property value of type string. In this example, the code first calls the Execute method of the Open dialog box, displaying the dialog box in your application. When you select a file name in the dialog box, that file name is specified as the parameter of the LoadFromFile method:

begin

 OpenDialog1.Execute;

 Memo1.Lines.LoadFromFile(OpenDialog1.FileName);     { the user specifies the parameter }

end;

You can read more about using the open dialog box (the TOpenDialog component) and the FileName property of an open dialog box in Delphi's online Help.

To read more about parameters, see "Passing parameters."


Calling functions

Section topics	See also

Like a procedure, a function executes code that performs a specific task. Also like a procedure, a function that is declared within an object, component, or control is called a method.

The difference between a function and procedure is that a function returns a value when it stops running, and a procedure doesn't. Your code can then use the value returned by the function to do one of two things:

Assign it to a property or variable

Use the value to determine the flow of code execution

Assigning a function's returned value

This code example appears in "Giving a variable a type":

procedure TForm1.AddButtonClick(Sender: TObject);

var

 X, Y, Sum: Integer;

begin

 X := 100;

 Y := 10;

 Sum := X + Y;

 Edit1.Text := IntToStr(Sum);

end;

The example uses the IntToStr function:

Edit1.Text := IntToStr(Sum);

IntToStr converts an integer value of type Longint into a string value. This is the declaration of the IntToStr function in Delphi's run-time library:

function IntToStr(Value: Longint): string;

All functions begin with the reserved word function. The word after the final colon in a function declaration is the data type of the value that is returned. The IntToStr function therefore returns a value of type string.

Once the code in the IntToStr function runs, the assignment statement assigns the returned string value to the Text property of an edit box named Edit1.

Value is the only parameter of the IntToStr function. It has a data type of Longint, an integer type. To use IntToStr, you can specify as the Value parameter

An actual integer value:

IntToStr(3)

A variable or property that is an integer type:

IntToStr(Sum)

An expression that is evaluated as an integer value (assume X and Y are integer values in this example):

IntToStr(X+Y)

To call a function,

Assign the returned value to a variable or property that is compatible with the returned value:

Edit1.Text := IntToStr(Sum);

Using functions that return a Boolean value to branch code

Some functions return a Boolean value, which is either True or False. Your code can use such a function to branch to various tasks and routines based on the value returned.

For example, the following form has a color dialog box and a button with this event handler for its OnClick event:

procedure TForm1.ChangeColorClick(Sender: TObject);

begin

if ColorDialog1.Execute then	{ if user clicks OK button }

  Form1.Color := ColorDialog1.Color

else

  Form1.Color := clRed;

end;

Example: using a function that returns a Boolean value:

The event handler uses the Execute method, a function that returns a Boolean value. You can either select a color in the dialog box and choose OK, or choose Cancel to exit the dialog box without selecting a color. If you choose OK, the ColorDialog1.Execute method returns True; if you choose Cancel, the method returns False. If ColorDialog1.Execute returns True, the form is colored with your color selection (Form1.Color is assigned the value you selected, which is ColorDialog1.Color); if it returns False, the form is colored red.

The compiler would interpret the code the same way if you wrote the Execute function like this:

if ColorDialog.Execute = True then ...


Controlling the flow of code execution

Section topics	See also

Object Pascal has several statements that direct the flow of code execution. Two are branching statements, and the other three are loop statements:

Branching statements

The if statement

The case statement

Looping statements

The repeat statement

The while statement

The for statement


The if statement

Section topics	See also

Object Pascal's if statement evaluates an expression and determines the flow of your code based on the result of that evaluation. If a certain condition is True, the code flow branches one way. If the condition is False, the flow branches in another direction. To see the syntax of an if statement, see the if reserved word topic in Delphi's online Help.

The following example uses a simple if statement.

An OKClick event handler is attached to the button:

procedure TForm1.OKClick(Sender: TObject);

begin

 if Edit1.Text = 'Saturday' then

   Label2.Caption := 'Why are you working today?';

end;

In this event handler, the equal sign operator (=) is used as a relational operator. It compares the value of the constant Saturday with the value of the Edit1.Text property.

The if statement in this handler is of the simplest form. The if statement begins with the reserved word if and is followed by an expression that results in a Boolean value: True or False. If the expression is True, the code following the reserved word runs. So if you type the word Saturday into the edit box control and click the OK button on the form, the message Why are you working today? appears in the second label.

Using an else part in an if statement

The logic for this simple application has a flaw. What if you type Saturday in the edit box and click the OK button one time, but then type Monday and click the OK button again? The Saturday message still remains in the second label. You could add an else part to the if statement to handle strings other than Saturday typed in the edit box.

For example, you could modify the event handler to look like this:

procedure TForm1.OKClick(Sender: TObject);

begin

 if Edit1.Text = 'Saturday' then

   Label2.Caption := 'Why are you working today?'

 else

   Label2.Caption := '';

end;

This if statement also includes the else reserved word. The statement following else runs if the following tested condition is False:

Edit1.Text = 'Saturday'

The statement in the else part clears the caption, because ''is an empty string.

Note:	In the if statement just shown, there is no semicolon until the end of the else part. If you put a semicolon before the else part, Delphi generates an error.

Writing multiple statements within an if statement

If you want more than one thing to happen when the tested condition in an if statement is True, you can write multiple statements in a begin..end block. This expanded handler now displays a message and also turns the form yellow:

procedure TForm1.OKClick(Sender: TObject);

begin

 if Edit1.Text = 'Saturday' then

 begin

   Label2.Caption := 'Why are you working today?';

   Form1.Color := clYellow;

 end

 else

   Label2.Caption := '';

end;

Each statement in the begin..end block ends in a semicolon. When you use multiple statements in the if part of an if statement, you still need to remember never to precede the else part with a semicolon. That is why no semicolon follows the first end reserved word in the previous code example.

Note:	You can use multiple statements any place that requires a statement as long as you start the statement part with begin and end it with end. Such a statement part is known as a compound statement.

Nesting if statements

Put if statements within if statements when the logic of your application requires it. For example, the OKClick handler shown here can now test three conditions:

procedure TForm1.OKClick(Sender: TObject);

begin

 if Edit1.Text = 'Saturday' then

   Label2.Caption := 'Why are you working today?'

 else

   if Edit1.Text = 'Sunday' then

     Label2.Caption := 'You should be resting'

   else

     if Edit1.Text = 'Monday' then

       Label2.Caption := 'Welcome to a new work week!'

     else

         Label2.Caption := '';

end;

As soon as a condition is met, the statement part runs and no further testing of conditions occurs. In other words, if you type Saturday in the edit box, the code displays the Saturday message in the label, and the rest of the if statement is ignored.

Note:	No semicolon appears until the end of the final else part. When you write nested if statements, choose a consistent, clear indentation style. This will help you and anyone else who reads your code see the logic of the if statement and how the code flows when your application runs. But remember that indentation does not affect program flow. If an if statement looks ambiguous, use begin..end blocks to clarify the flow, as shown in the next example.

You can also nest if statements containing else parts within if statements that don't have else parts. Your nested if statement might have this structure:

if ConditionA = True then

 if Condition B = True then

   DoThis

 else

   DoThat;

Be aware that if you do this, the compiler considers the else part to belong to the inner-most if statement. If you want to call DoThat whenever ConditionA is False regardless of the value of ConditionB, write your code like this:

if ConditionA = True then

 begin

   if ConditionB = True then

     DoThis

 end

else

 DoThat;

In this case, using the begin and end reserved words forces the else part to be linked with the first if statement.


The case statement

Section topics	See also

Your application can also use a case statement to branch to the appropriate lines of code. The case statement is often preferable to a series of if statements when the variable or property being evaluated is an ordinal type. The logic of a case statement is usually easier to see than in complex nested if statements, and the code runs more quickly.

To see the syntax of the case statement, look up the case reserved word topic in Delphi's VCL online Help.

Here's an example of the case statement, based on the following form:

This event handler is attached to the button's OKClick event:

procedure TForm1.OKClick(Sender: TObject);

var

 Number: Integer;

begin

 Number := StrToInt(Edit1.Text);

 case Number of

   1, 3, 5, 7, 9: Label2.Caption := 'Odd digit';

   0, 2, 4, 6, 8: Label2.Caption := 'Even digit';

   10..100: Label2.Caption := 'Between 10 and 100';

 else

   Label2.Caption := 'Greater than 100 or negative';

 end;

end;

When you run the application, enter a number, and choose OK, the string representation of the number is converted to an integer and assigned to the Number variable. The case statement then uses the value of Number to determine which statement within the case statement runs. In this line of code, the statement after the colon runs, and the Label2.Caption receives the string 'Odd digit' if the value of Number is 1, 3, 5, 7, or 9:

1, 3, 5, 7, 9: Label2.Caption := 'Odd digit';

Like the if statement, the case statement has an optional else part. Case statements end with the end reserved word.

Note:	If you want to include more than one statement in the statement part of a case statement (the part following the colon (:)), place the begin and end reserved words around the multiple statements. For example,

case Number of

 1, 3, 5, 7, 9:

 begin

   Label2.Caption := 'Odd digit';

   Form1.Color := clBlue;

 end;

 2, 4, 6, 8: Label2.Caption := 'Even digit';

 ...


Writing loops

Section topics	See also

Object Pascal has three statements that execute blocks of code repeatedly:

The repeat statement

The while statement

The for statement

The repeat statement

The repeat statement repeats a statement or series of statements until some condition is True. The statement begins with the repeat reserved word and ends with the reserved word until, followed by the condition that is evaluated. The condition is always a Boolean expression.

To see the syntax of the repeat statement, look up the repeat reserved word topic in online Help.

Here is a sample form that uses a repeat statement:

This event handler is attached to the button:

procedure TForm1.RepeatButtonClick(Sender: TObject);

var

 I: Integer;

begin

 I := 0;

 repeat

   I := I + 1;

   Writeln(I);

 until I = 10;

end;

When the application is run, and the Repeat button is clicked, the numbers 1 through 10 are written down the edge of the . The Boolean expression I = 10 isn't evaluated until the end of the repeat..until block. This means that the statements within the repeat..until block always run at least one time.

For more information about repeat statements, see Delphi's online Help.

The while statement

While the repeat statement evaluates a condition at the end of the loop, the while statement evaluates a condition at the beginning of the loop. It starts with the while reserved word and the tested condition, which must always be a Boolean expression. If the condition is met (the expression is True), the code in the while statement runs until it reaches the end of the statement and the expression is tested again. As soon as the expression is False, the while statement stops running, and execution continues after the while loop.

This example of a while loop uses the same example form as the repeat loop did. This is how the finished form appears:

This event handler is attached to the While button:

procedure TForm1.WhileButtonClick(Sender: TObject);

var

 J: Integer;

begin

 J := 0;

 while J < 10 do

 begin

   J := J + 1;

   Writeln(J:50);

 end;

end;

When the application is run and the While button is clicked, the following Boolean expression is examined first:

J < 10

If it is True, the remainder of the code within the while statement runs, and then the expression is evaluated again. If the expression is False, the while statement stops running.

When you click the While button, once again the numbers 1 through 10 are written. This time they appear 50 character spaces to the left of the edge of the .

If you change the statement I := 0 to I := 10 in the RepeatButtonClick event handler, and then change the statement J := 0 to J := 10 in the WhileButtonClick handler, clicking a button gives the following results:

If you click the Repeat button, the statements within the repeat statement run, even though I already equals 10. This happens because the Boolean expression isn't evaluated until the end of the repeat statement. The loop keeps running until I = 10 once again.

If you click the While button, the statements within the while statement don't run at all. Because J already equals 10, the Boolean expression is True, and the while statement stops running.

For more information about while statements, see Delphi's online Help.

The for statement

So far this topic has discussed two kinds of loops, the repeat statement and the while statement. The repeat statement runs until a condition is True, and the while statement runs while a condition is True. The for statement is the third type of loop in Object Pascal. The code within a for loop runs a specific number of times.

The value of a control variable, which is really just a counter, determines how many times a for statement runs. You need to declare a variable for each for loop you use in your code. The variable is usually an integer type, but it can also be a Boolean, character, enumerated, or subrange type.

The simplest way to understand a for statement is to look at an example. The code shown here would print the numbers 1 to 5, each on a separate line. X is the control variable:

var

 X: Integer;

for X := 1 to 5 do

 Writeln(X);

The for statement defines an initial value (1) and a final value (5) for variable X. When the for statement begins running, X is assigned the initial value, and the Writeln procedure runs, displaying the current value of X. The value of X is then incremented by 1. Because the value of X, now 2, is not greater than the final value (5), the Writeln procedure runs again, this time displaying the value of X as 2. This process continues until the value of X becomes greater than the final value, 5, and the execution of the for statement ends.

It's important that you never change the value of the control variable in the middle of the loop. Doing so will cause errors.

A counting example

This is a variation of the same for loop just discussed. It uses a string grid control and a button the user clicks to start the counting process.

Here's what the sample form looks like:

An event handler, CountButtonClick, is attached to the button:

procedure TForm1.CountButtonClick(Sender: TObject);

var

 Col: Integer;

begin

 for Col := 1 to 5 do

     StringGrid1.Cells[Col, 1] := IntToStr(Col);

end;

This event handler uses the Cells property, which is a two-dimensional array of strings indexed by a column and row value. (If you are unfamiliar with arrays, you can read about them in "Array types.") Each cell in the string grid is represented in the Cells property array. To access a particular cell in the grid, you specify the cell's column and row value. For example, the top left cell in the grid is Cells[0,0], meaning that the cell is in column 0 and row 0 (the first position in the Cells array is 0).

When you run the application and click the Count button, the for statement in the CountButtonClick handler displays the numbers 1 through 5, one in each cell, in row 1 of the string grid, starting with column 1. As the for statement runs, the column number increases, but the row number remains the same (1).

The for statement can also decrement values using the downto reserved word. To do so, the CountButtonClick event handler would have to change to look like this:

procedure TForm1.CountButtonClick(Sender: TObject);

var

 Col: Integer;

begin

 for Col := 5 downto 1 do

     StringGrid1.Cells[Col, 1] := IntToStr(Col);

end;

When you run the application and click the Count button, the end result is the same as before, but the handler displays the numbers beginning with column 5 and proceeding to column 1. As the code runs, the Col variable is assigned the initial value of 5, which is then decremented until the value of Col is less than the final value (1).

Using nested for loops

Writing a for loop within another for loop is very useful when you want to display data in a table or a grid. This example uses the string grid. The code displays simple column and row titles in the nonscrolling or fixed regions of the grid, and then it displays the column and row coordinates for each cell. When you click the Show Coordinates button, this appears in the grid:

StringGrid1 has to following properties: RowCount is 6, ColCount is 6, DefaultColWidth is 105, and the Name property of Button1 is ShowCoordinatesButton.

This event handler is attached to the Show Coordinates button:

procedure TForm1.ShowCoordinatesButtonClick(Sender: TObject);

var

 Col, Row: Integer;

begin

 for Col := 1 to 5 do

   StringGrid1.Cells[Col, 0] := 'Col ' + IntToStr(Col);

 for Row := 1 to 5 do

   StringGrid1.Cells[0, Row] := 'Row ' + IntToStr(Row);

 for Col := 1 to 5 do

   for Row := 1 to 5 do

     StringGrid1.Cells[Col, Row] :=

       'Col ' + IntToStr(Col) + ', ' + 'Row ' + IntToStr(Row);

end;

This event handler contains four for statements. The first for statement writes the string 'Col ' plus the current value of the Col variable in the row 0 once the value of Col is converted to a string:

for Col := 1 to 5 do

 StringGrid1.Cells[Col, 0] := 'Col ' + IntToStr(Col);

The second for statement writes the string 'Row ' plus the current value of the Row variable in the column 0 once the value of Row is converted to a string:

for Row := 1 to 5 do

 StringGrid1.Cells[0, Row] := 'Row ' + IntToStr(Row);

The fourth for statement is nested within the third for statement. Together, the two for statements display each cell's column and row coordinates within the cell:

for Col := 1 to 5 do

   for Row := 1 to 5 do

     StringGrid1.Cells[Col, Row] :=

       'Col ' + IntToStr(Col) + ', ' + 'Row ' + IntToStr(Row);

Col receives the initial value of 1, indicating the first scrolling column of the grid. Before the for statement actually does something, the second for loop begins. Row is assigned the initial value of 1, indicating the first row of the scrolling column of the grid. The code then writes a string in the column 1 and row 1. The string consists of the substring 'Col ', the string value of the Col variable, the substring ', ', the substring 'Row ', and the string value of the Row variable. The actual value in Cells[1, 1] is this string: 'Col 1, Row 1'.

The code continues to execute the nested for loop, moving to the next row within the same column as the value of the Col variable is still 1. The nested for loop ends when the value of Row has increased from 1 to 5 and a value is written within each row of column 1. Then the third for loop continues to run as the Col variable is assigned the next highest value, at which time the nested for loop begins again with the new Col value.

Deciding which loop to use

This topic has described several kinds of Object Pascal loops. How do you decide which kind to use? Here are some guidelines to help you decide:

Use the for loop if you know how many times you want a loop to repeat. The for loop is very fast and efficient.

Use the repeat..until loop if you don't know how many times you want the loop to repeat, but you do know it must run at least once.

Use the while..do loop if you don't know how many times you want the loop to repeat, and it's possible you don't want the loop to run at all.


Object Pascal blocks

Section topics	See also

Blocks are an important concept in Object Pascal. They provide the structure of your application, and they determine the scope of variables, properties, and so on. You can read about scope in "Understanding scope."

An Object Pascal block is made up of two parts: an optional declaration part and the statement part. If there is a declaration part, it always precedes the statement part.

A declaration informs the compiler of the item being declared. Object Pascal has:

Variable declarations

Constant declarations

Type declarations

Label declarations

Procedure, function, and method declarations

Following the declaration part is the statement part of the block. The statements of a block describe algorithmic actions that can be executed. If a block contains a declaration part, the statement part uses the items declared in the declaration part of the same block.


Blocks within event handlers

Section topics	See also

One of the most common kinds of blocks you encounter in Delphi are the blocks within event handlers. The following simple event handler doesn't have a declaration part, but it does have a statement part:

procedure TForm1.Button1Click(Sender: TObject);

begin	{ The block starts here with the statement part }

 Edit1.Text := 'Welcome to Delphi';

end;	{ The block ends here at the end of the statement part }

The beginning of the statement part is marked with the reserved word begin. The end of the statement part is marked with the reserved word end.

Blocks can contain optional declaration parts. This is a revised version of the event handler that now includes a variable declaration part:

procedure TForm1.Button1Click(Sender: TObject);

var	{ The block begins here with the start of the declaration part }

 Name: string;

begin	{ The statement part of the block begins }

 Name := Edit1.Text;

 Edit2.Text := 'Welcome to Delphi, ' + Name;

end;	{ The end of the block }

As shown in this event handler, the variable declaration part starts with the reserved word var and continues until the beginning of the statement part.

As described earlier, event handlers are Object Pascal procedures. Procedures that aren't event handlers also have blocks that look exactly like the blocks in event handlers. You can read more about procedures and also about functions, which are very similar to procedures, in "Writing a procedure or function."


Blocks within units

Section topics	See also

Units also have blocks that are made up of a declaration part and a statement part. While units have a few unique parts of their own, the notion of a block is still present. For example:

unit Unit1;

interface

uses Windows, Classes, Graphics, Forms, Controls, Apps;

type	{ Beginning of the declaration part and the unit block }

 TForm1 = class(TForm)

   Edit1: TEdit;

   Edit2: TEdit;

   Button1: TButton;

   procedure Button1Click(Sender: TObject);

 private

   { Private declarations }

 public

   { Public declarations }

 end;

var

 Form1: TForm1;	{ The end of the declaration part }

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);	{ Beginning of the event handler block }

var

 Name: string;

begin

 Name := Edit1.Text;

 Edit2.Text := 'Welcome to Delphi, ' + Name;

end;	{ End of the event handler block }

end.	{ The end of the unit block }

For more information about units, see "Understanding Object Pascal units." You can also read about units and study the accompanying syntax diagrams in online Help.


Blocks within blocks

Section topics	See also

Blocks can contain other blocks. For example, in the previous example, the block of the Button1Click event handler exists within the unit block, which exists within the project block. Here is a visual representation of the blocks within the application so far:

All Delphi applications have the same basic structure. As your application becomes more complex, you are adding new blocks of code, such as new event handlers to a unit, or new units to a project.


Understanding scope

Section topics	See also

The scope of a variable, constant, method, type, or other identifier defines where the identifier is active. The identifier is local in scope to the smallest block in which it is declared.

When your application is executing code outside the block in which an identifier is declared, that identifier is no longer in scope. That means the identifier is no longer available to the executing code. Only when code execution reenters the same block as the identifier declaration can the code access the identifier again.

You were introduced to blocks in the previous topic. Imagine that you have a project that contains two units. Each of those units contains three event handlers, or procedures. Here is your project depicted graphically:

Each rectangle in the diagram represents a block. If you declare a variable within procedure D, only the code within procedure D can access that variable, as that variable is local in scope to procedure D. If you declare a variable in unit B that is not in any procedure, all procedures D, E, and F can use the variable, because all these procedures are within the scope of unit B. The variable is now global in scope to the procedures, but local in scope to unit B.

In general, declare identifiers that are as local in scope as possible. This gives the data in your application increased protection from being modified when you do not intend it to be. Only when you need to share data between different blocks should you consider using a more global scope.


Accessing declarations that are not within scope

Section topics	See also

You can refer to declarations in blocks that are not within the scope of the current block. For example, if you create an event handler that calculates an interest payment in one unit, it is possible for another unit to access that event handler.

To access a declaration that is not within scope,
Preface the declaration name with the name of the block in which it is declared and a period(.).

For example, to call the CalculateInterest procedure that is declared in Unit1 from Unit2, make the call like this:

Unit1.ComputeInterest(Principal, InterestRate: Double);

You would also need to add Unit1 to the uses clause of Unit2. For more information about adding units to a uses clause, see "How units are used."


A scope example

Section topics	See also

This example illustrates local and global scope. It uses the following form. Each time the user enters two numbers in the first two edit boxes and chooses Multiply, the application multiplies the two numbers together and displays the result in the The Answer edit box.

This OnClick event handler is attached to the Multiply button:

procedure TForm1.MultiplyClick(Sender: TObject);	{ Multiplies two numbers together }

var

 FirstNumber, SecondNumber: Integer;

begin

 FirstNumber := StrToInt(Edit1.Text);

 SecondNumber := StrToInt(Edit2.Text);

 Edit3.Text := IntToStr(FirstNumber * SecondNumber);	{ Displays answer in Edit3 }

end;

When you run the application, you can type one integer in the first edit box and a second integer in the second edit box. When you click the Multiply button, the third statement multiplies the two numbers together and displays the result in the third edit box.

You could add another button to the form and change its Name property to Divide.

Then create this event handler:

procedure TForm1.DivideClick(Sender: TObject);

begin

 FirstNumber := StrToInt(Edit1.Text);

 SecondNumber := StrToInt(Edit2.Text);

 Edit3.Text := IntToStr(FirstNumber div SecondNumber);

end;

If you try to compile or run the application now, you get an Unknown Identifier error message. Even though you already declared the FirstNumber and SecondNumber variables in the MultiplyClick event handler, the DivideClick event handler can't use them. Code in your application can't access variables outside the event handler in which they were declared. In fact, these variables cease to exist in memory when code execution continues outside the event handler. This is true for all identifiers declared within any routine, whether it is an event handler, procedure, function, or method. Such identifiers are called local in scope.

You could choose one of these two methods to make the application run correctly:

You could redeclare the FirstNumber and SecondNumber variables in the DivideClick event handler. Again, the variables are local in scope:

procedure TForm1.DivideClick(Sender: TObject);

var

 FirstNumber, SecondNumber: Integer;

begin

 FirstNumber := StrToInt(Edit1.Text);

 SecondNumber := StrToInt(Edit2.Text);

 Edit3.Text := IntToStr(FirstNumber div SecondNumber);

end;

You could move the variable declaration part out of the MultiplyClick event handler to an area above all event handlers, but after the implementation line and the {$R *.DFM} directive. Your code would look like this, beginning with the implementation part:

implementation

{$R *.DFM}

var

 FirstNumber, SecondNumber: Integer;	{ Variables global to the event handlers }

procedure TForm1.MultiplyClick(Sender: TObject);	{ Multiplies two numbers together }

begin

 FirstNumber := StrToInt(Edit1.Text);

 SecondNumber := StrToInt(Edit2.Text);

 Edit3.Text := IntToStr(FirstNumber * SecondNumber);	{ Displays result in Edit3 }

end;

procedure TForm1.DivideClick(Sender: TObject);	{ Divides first number by the second }

begin

 FirstNumber := StrToInt(Edit1.Text);

 SecondNumber := StrToInt(Edit2.Text);

 Edit3.Text := IntToStr(FirstNumber div SecondNumber);	{ Displays result in Edit3 }

end;

...

If you choose the second method, FirstNumber and SecondNumber are no longer local in scope to the event handlers. Because any event handler can now access these variables, even those you might add in the future, they are called global in scope, or global variables. The variables are local in scope to the unit, however, because the variables are declared within the unit block.

As stated earlier, you should try to keep your variables as local as possible. This protects you from forgetting where a variable is used and getting side effects you don't want when your code changes the value of the variable. Therefore, the first method discussed earlier is probably the best choice. You should consider variables with a broader scope only when your application needs to share data between different blocks.

Because the code now can access the necessary variables, regardless of which method you chose, the example works correctly--almost. If the FirstNumber value isn't evenly divisible by the SecondNumber value, the fractional part of the result is discarded, because the example is using only integer values.

Redeclaring identifiers in a different scope

It's perfectly legal to declare an identifier in more than one place in your application, as long as the scope of the identifier differs. The compiler always accesses those variables that are the most local in scope.

This topic has already discussed how two event handlers can declare the same variables, FirstNumber and SecondNumber. Because any identifiers declared within MultiplyClick and DivideClick are not within the same scope, the two can have identifiers with the same name declared within them.

If FirstNumber were declared globally within the scope of the unit and also declared locally within the scope of an event handler within the unit, your application would compile without problems. The compiler, however, uses the most local variable to which it has access each time it encounters a FirstNumber variable in code.

Using a global variable

Suppose you wanted to keep track of the number of calculations performed while the application was running. You could modify the form to look like this:

Then add these lines of code to each event handler:

Count := Count + 1;

Counter.Text := IntToStr(Count);

This code adds one to the Count variable each time one of the buttons is clicked and displays the result in the Counter edit box.

If you try to run the application now, Delphi reminds you that you haven't declared the Count variable yet. You could declare Count in each of the event handlers and give it an initial value of 0. This is how the DivideClick event handler would look:

procedure TForm1.DivideClick(Sender: TObject);

var

 FirstNumber, SecondNumber, Count: Integer;

begin

 Count := 0;

 FirstNumber := StrToInt(Edit1.Text);

 SecondNumber := StrToInt(Edit2.Text);

 Edit3.Text := IntToStr(FirstNumber div SecondNumber);

 Count := Count + 1;

 Counter.Text := IntToStr(Count);

end;

The problem is that each time one of the event handlers runs, Count is reset to 0, and then incremented by one. The application would never display the number of calculations as greater than one!

In this case, the application really needs a global variable to function properly.

Count needs to be declared outside of the scope of the event handlers, and within the scope of the unit in the unit's implementation part:

...

implementation

{$R *.DFM}

var

 Count: Integer;

procedure TForm1.MultiplyClick(Sender: TObject);

...

Also, to prevent Count from being reset to 0 each time the Multiply or Divide button is clicked, Count must be initialized in the initialization part of the unit. To create an initialization part, the following code adds the reserved word initialization and the code that initializes Count above the final end reserved word at the bottom of the unit. The block looks like this:

initialization

 Count := 0;

end.

Now if you run the application and enter values in Edit1 and Edit2, each time you click one of the buttons, one is added to the number of calculations.

Because both event handlers need to access and change the value of Count, Count needs to be declared as a global variable to the event handlers.

Note:	A more object-oriented way of accomplishing the same goal would be to make Count a field of the form object. All the event handlers of the form could then access the Count field. You can read about objects and fields in "Programming with Delphi objects.".


Writing a procedure or function

Section topics	See also

Much of the code you write while developing a Delphi application is contained within event handlers Delphi creates for you. Still, there are times when you want to write your own procedures and functions that aren't event handlers.

For example, if you find yourself writing the same code over and over to perform some common task within event handlers, consider moving that code from the event handlers into a procedure or function outside the handlers. Then any event handler can call the procedure or function as it does any other existing procedure or function. You derive two benefits: you need to write the code only once, and the code in your event handlers is less cluttered.

This example continues with the scope example presented in the previous topic. Here is the code of the example as it exists currently, beginning with the implementation part:

...

implementation

{$R *.DFM}

var

 Count: Integer;

procedure TForm1.MultiplyClick(Sender: TObject);

var

 FirstNumber, SecondNumber: Integer;

begin

 FirstNumber := StrToInt(Edit1.Text);

 SecondNumber := StrToInt(Edit2.Text);

 Edit3.Text := IntToStr(FirstNumber * SecondNumber);

 Count := Count + 1;

 Counter.Text := IntToStr(Count);

end;

procedure TForm1.DivideClick(Sender: TObject);

var

 FirstNumber, SecondNumber: Integer;

begin

 FirstNumber := StrToInt(Edit1.Text);

 SecondNumber := StrToInt(Edit2.Text);

 Edit3.Text := IntToStr(FirstNumber div SecondNumber);

 Count := Count + 1;

 Counter.Text := IntToStr(Count);

end;

initialization

 Count := 0;

end.

So far, the application doesn't allow users to make any data entry errors. For example, if a user neglects to enter a value in one of the two edit boxes before clicking a button, the application stops running. To ensure that this doesn't happen, you would have to write some code to check for a value in the edit boxes and, if there is none, prompt the user to enter one. Instead of writing this code twice--once in each event handler--you could write it once and call the routine from the event handler.

Here is the new NoValue function written in the implementation part of the unit and placed above all event handlers:

function NoValue(AnEditBox: TEdit): Boolean;

begin

 if AnEditBox.Text = '' then

 begin

   AnEditBox.Color := clRed;

   AnEditBox.Text := 'Enter a value';

   Result := True;

 end

 else

 begin

   AnEditBox.Color := clWindow;

   Result := False;

 end;

end;

The NoValue function determines if the edit box passed to the function is empty or not. If it is, the edit box color turns red, the user is prompted to enter a value, and the function returns True. If the edit box isn't empty, the edit box color is set to the Windows system window color, thereby changing the edit box color back to normal if it happened to be red, and the function returns False.

Now the NoValue function must be called from the two event handlers. Here is the call (actually two of them) from the MultiplyClick event handler:

procedure TForm1.MultiplyClick(Sender: TObject);

var

 FirstNumber, SecondNumber: Integer;

begin

 if NoValue(Edit1) or NoValue(Edit2) then	{ This line calls NoValue twice }

   Exit;	{ If an edit box is empty, quit this event handler }

 FirstNumber := StrToInt(Edit1.Text);

 SecondNumber := StrToInt(Edit2.Text);

 Edit3.Text := IntToStr(FirstNumber * SecondNumber);

 Count := Count + 1;

 Counter.Text := IntToStr(Count);

end;

If either function call returns True (one or both of the edit boxes are empty), the Exit procedure runs. Exit is a routine in the run-time library that stops executing the code in the current block. This means that none of the remaining lines of code in the MultiplyClick event handler run.

When you run the application now, the user is notified when either edit box is left empty.

A procedure or function consists of:

A procedure or function heading

A block of code


Writing a procedure or function header

Section topics	See also

Every procedure or function begins with a header that identifies the procedure or function and lists the parameters the routine uses, if any. A procedure begins with the procedure reserved word. A function begins with the function reserved word.

The parameters are listed within parentheses. Each parameter has an identifying name and usually has a type. Parameters in a parameter list are separated from one another by a semicolon. For example,

procedure ValidateDate(Day: Integer; Month: Integer; Year: Integer);

You can combine parameters of the same type, like this:

procedure ValidateDate(Day, Month, Year: Integer);

You can read more about using parameters in "Passing parameters."

Functions have one additional element in their headers: a return value type. Specify the type of the value returned by the function, separated from the rest of the header with a colon. For example, this function returns a value of type Double:

function CalculateInterest(Principal, InterestRate: Double): Double;

End all headers with a semicolon.


Writing a procedure or function block

Section topics	See also

As discussed in "Object Pascal blocks," all blocks have two parts: an optional declaration part and a statement part.

Declarations within a procedure or function block

Declaration parts within a procedure or function block can contain:

Type declarations

Variable declarations

Constant declarations

Type declarations

Delphi applications can create new types beyond the predefined data types that are part of the Object Pascal language. You can read about creating user-defined types in "Defining new data types," but here you can see what a type declaration part can look like. A type declaration part begins with the reserved word type. These are some type declarations:

type

 TCount = Integer;

 TPrimaryColor = (Red, Yellow, Blue);

 TTestIndex = 1..100;

 TTestValue = -99..99;

 TTestList = array[TTestIndex] of TTestValue;

 TCharVal = Ord('A')..Ord('Z');

 TDays = (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);

An equal sign (=) follows the type identifier. The rest of the declaration defines the values that make up the new type. A variable of type TNumber must be an Integer value. A variable of type TPrimaryColor can be only red, yellow, or blue. A variable of type TCharVal must fall within the range of values returned by the Ord function on the capital letters A through Z, and so on.

Note that each type has a name that begins with the letter T. This isn't necessary, but it's the convention the Delphi documentation uses, and you'll find it helpful in distinguishing type names from other identifiers if you use it, too.

Just like variable and constant declarations, type declarations can be global or local in scope, depending on where you place the declaration. For example, if you declare the type TDays within a procedure, the TDays type exists only when that procedure runs, and it is therefore local in scope. If you place a type declaration at the top of the implementation part, all event handlers, and other procedures, and functions can use it.

Variable and constant declarations

Declaring variable and constants within event handlers is discussed in "Declaring variables," and in "Declaring constants." Because event handlers are procedures, you should already know how to create variable and constant declarations for procedures and functions.

Ordering the declaration parts

Traditionally, any type declarations appear in a procedure or function before variable declaration parts, and any variable declarations appear before constant declarations. This isn't required, though, and you can mix declaration parts any way you choose. You can also have more than one declaration part of the same kind. For example, you can have two or more variable declaration parts. The only rule you must remember is that declarations occur after the procedure or function header and before the statement or code part of the routine.

Writing the statement part

The statement part of a procedure or function begins with the reserved word begin and ends with the reserved word end, followed by a semicolon. All the code within the begin and end block are the statements of a procedure or function. As discussed earlier, statements can be assignment statements, procedure or function calls, if or case statements, or loops.

Assigning a return value in a function

Because a function returns a value, you need to ensure that you assign that return value. You can do this in either of two ways:

Assign the value to be returned to the name of the function.

Assign the value to be returned to the Result variable.

The following example shows you the first way; the value to be returned is assigned to the name of the function:

function CalculateInterest(Principal, InterestRate: Double): Double;

begin

 CalculateInterest := Principal * InterestRate;

end;

The next example accomplishes the same thing, but the value is assigned to a Result variable that all functions have:

function CalculateInterest(Principal, InterestRate: Double): Double;

begin

 Result := Principal * InterestRate;

end;

You don't have to declare the Result variable--it is automatically available for you to use. Using Result is a shortcut that makes your code just a little easier to understand.

No matter which method you choose, the function call is still the same. For example, this function call is the same regardless of whether the CalculateInterest function internally assigns a value to CalculateInterest or Result:

InterestEarned := CalculateInterest(1000, 0.08);


Positioning a procedure or function in your code

Section topics	See also

Procedures and functions are placed in the implementation part of a unit. All identifiers declared in the implementation part can be used only by the code contained within that unit.

You can set up procedures and functions so that code in other units can use the procedures and functions you write.

To make a procedure or function available to other units:

1.	Put the header of the procedure or function in the interface part of the unit.

By placing the header in the interface part, you are declaring the routine in the interface part, thereby making the routine available to any unit that uses this unit.

2.	Place the entire procedure or function, repeating the header, in the implementation part of the unit.

3.	Add the name of the unit in which you declared the routine to the uses clause of the unit that needs to access the routine.

You can read more about units, the uses clause, and how to use routines declared in other units in "Understanding Object Pascal units."

For example, the NoValue function described in "Writing a procedure or function" could be declared in the interface part of the unit. Then you could place the body of the routine, or its implementation, in the implementation part of the unit. This is a skeleton of how the unit would look:

unit Unit1;

interface

uses

...

type

 TForm1 = class(TForm)

   Multiply: TButton;

   Divide: TButton;

   procedure MultiplyClick(Sender: TObject);

   procedure DivideClick(Sender: TObject);

 ...

 end;

var

 Form1: TForm1;

function NoValue(AnEditBox: TEdit): Boolean;

implementation

function NoValue(AnEditBox: TEdit): Boolean;

...

end;

procedure TForm1.MultiplyClick(Sender: TObject);

...

end;

procedure TForm1.DivideClick(Sender: TObject);

...

end;

end.

If the NoValue function is not declared in the interface part, but in the implementation part instead, only this unit and no other can use NoValue.

When Delphi creates an event handler, the event handler procedure is declared in the object type declaration. Because the object type declaration is in the interface part of the unit, any other unit can call the event handler. For more information about the interface and implementation parts of a unit, see "Understanding Object Pascal units." You can also read more about object type declarations in "Programming with Delphi objects."

The NoValue function as it appears in "Writing a procedure or function" is an example of a routine that is not declared separately from its implementation. Because you want only the code within the event handlers to be able to use NoValue, you would not declare it in the interface part, but in the implementation part, as the code in the event handlers is in the implementation part.

Note:	NoValue appears before the event handlers that use it. If the NoValue function is declared and implemented after the event handlers, the compiler reports the NoValue call in the handlers to be an unknown identifier.

Wherever you first declare a procedure or function, you should place either the declaration or the procedure or function itself above all routines that use them, whether they are event handlers or routines you write entirely on your own. In other words, a routine can't use another routine unless the second routine has already been declared. This is in keeping with the Object Pascal rule that states you can't use an identifier until you have declared it.

Forward declarations

There is an exception to this rule, however, that allows routine A to call routine B, and then while routine B is running, routine B calls routine A. This type of calling is called mutual recursion.

To make such mutual recursion possible, you need to declare a routine as a forward declaration. When you declare a routine using the forward standard directive, you can place the implementation of the procedure or function anywhere you choose--between event handlers, after other procedures and functions that call the routine, and so on. If the NoValue declaration is declared as a forward declaration, it looks like this:

function NoValue (AnEditBox: TEdit): Boolean; forward;

A forward declaration uses the forward standard directive. The declaration doesn't include the implementation of the procedure. Instead, the NoValue implementation has to appear somewhere after the forward declaration.

The implementation of NoValue can appear after other procedures, functions, event handlers, and methods that might call it, however.

This is an example of two procedures that call each other recursively. The form in this application has one button on it. When you click the button, the two procedures begin calling one another.

unit Unit1;

interface

uses Windows, Classes, Graphics, Forms, Controls, Apps;

type

 TForm1 = class(TForm)

   Button1: TButton;

   procedure Button1Click(Sender: TObject);

 private

   { Private declarations }

 public

   { Public declarations }

 end;

var

 Form1: TForm1;

implementation

{$R *.DFM}

var

 Alpha: Integer;

procedure Test2(var A : Integer); forward;	{ Test2 is declared as a forward procedure }

procedure Test1(var A: Integer);

begin

 A := A - 1;

 if A > 0 then

   Test2(A);	{ A call to the Test2 procedure }

 Writeln(A);

end;

procedure Test2(var A: Integer);

begin

 A := A div 2;

 if A > 0 then

   Test1(A);	{ A call to the Test1 procedure }

 Writeln(A);

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

 Alpha := 15;	{ The Alpha variable receives a starting value }

 Test1(Alpha);	{ The first call to the Test1 procedure }

end;

end.

The code in the OnClick event handler for the button gives the Alpha variable a value and then calls the Test1 procedure. Test1 subtracts one from the value of the Alpha variable and then tests Alpha to determine if it is greater than 0. If it is, Test1 calls the Test2 procedure. Test2 divides the value of the Alpha variable by 2 and then tests Alpha to determine if it is greater than 0. If it is, Test 2 calls Test1. The two procedures continue to call each other until Alpha is no longer greater than 0, at which time the Writeln statements of the procedures run.

A more detailed discussion of mutual recursion and how it works is beyond the scope of these topics. The important point to note is that the Test2 procedure is declared as a forward procedure before the implementations of Test1, Test2, and the Button1Click event handler. If Test2 is not declared as a forward procedure, the project will not compile, because the code would then be calling Test2 before it is declared.


Passing parameters

Section topics	See also

When your code calls a procedure or function, it often uses parameters to pass data to the called procedure or function. The topic "Calling procedures with parameters," describes how to use a parameter in calling a procedure; the topic "Writing a procedure or function header," describes how to create a parameter list in a procedure or function header. This topic describes the most common kinds of parameters your procedures and functions can use:

Value parameters

Variable parameters

Constant parameters

Any parameters specified by the calling procedure or function are called the actual parameters. The corresponding parameters in the called procedure of function are called the formal parameters. So, in this procedure call, the Edit1 parameter is the actual parameter:

ColorIt(Edit1);

The corresponding AnEditBox parameter in the called procedure declaration is the formal parameter

procedure ColorIt(AnEditBox: TEdit);

Value parameters

With the exception of the code example in "Forward declarations," value parameters are the only type of parameters discussed in these topics so far. A formal value parameter is a copy of the actual value parameter. In other words, the compiler puts the actual parameter at one memory address, and the formal parameter at another memory address. Therefore, the called procedure or function can change the value of the formal parameter, yet the value of the actual parameter remains unchanged.

Here's an example of this:

The button has the following OnClick event handler:

procedure TForm1.Button1Click(Sender: TObject);

var

 Number: Integer;

begin

 Number := StrToInt(Edit1.Text);

 Calculate(Number);

 Edit2.Text := IntToStr(Number);

end;

The handler retrieves the number the user enters in the first edit box and assigns it to the Number variable. This variable is passed to the Calculate procedure as Calculate is called. The last line displays the value of Number in the second edit box.

The Calculate procedure needs to be placed above the event handler in the code, but below the implementation reserved word:

procedure Calculate(CalcNo: Integer);

begin

 CalcNo:= CalcNo * 10;

end;

In the Calculate procedure, the value in the CalcNo parameter changes. By the end of the procedure, the value of CalcNo is ten times greater than it was when Calculate began.

Suppose you run the application and enter a number in the Edit1 edit box.

The value you enter in the Edit1 edit box is the value of Number before Number is passed to the Calculate procedure. The value that appears in the Edit2 edit box is the value of Number after the Calculate procedure runs. The two values of Number are the same!

Because the CalcNo parameter is only a copy of the Number parameter, Number remains unchanged, while the value of CalcNo changes considerably. The application never displays the value of CalcNo.

A couple of simple illustrations can demonstrate what is happening. Number and CalcNo each have a separate address in the computer's memory. The boxes in the following drawings represent memory locations. When Calculate is called, the value of Number is copied into the CalcNo memory location.

The value of Number and CalcNo when Calculate begins running:

Because the Calculate procedure changes the value of the CalcNo parameter, the values of Number and CalcNo are no longer the same when Calculate stops running.

The values of Number and CalcNo after Calculate runs:

Value parameters can be any expression. To read about what constitutes an expression, see the "Expressions" topic in the VCL online Help.

Variable parameters

Sometimes you want the value of a passed parameter to change as the called procedure or function processes it. In these cases, you need to use a variable parameter. Precede the formal parameter in the called routine with the reserved word var in the parameter list. For example,

procedure Calculate(var CalcNo: Integer);

This variable parameter is a reference to the actual parameter, which is Number in the example. It does not occupy a separate location in memory, but instead, points to the actual parameter.

When a variable parameter is passed, any changes made to the formal parameter are reflected in the actual parameter because both parameters refer to the same value.

With var added to the Calculate procedure, if you run the application and enter a number in the Edit1 edit box, the Calculate procedure does indeed change the value of the Number variable in the OnClick event handler. Because the Number actual parameter and the formal CalcNo parameter now refer to the same variable, the Number value changes in the second edit box when Calculate runs.

These drawings demonstrate what happens. When Calculate is called, the value of Number and the value of CalcNo are the same, because both the actual and the formal parameters refer to the same memory location.

The value of Number and CalcNo when Calculate begins running:

After the Calculate procedure runs, the Number and CalcNo parameters still refer to the same value, even though that value has changed.

The value of Number and CalcNo after Calculate runs

:

Constant parameters

If a formal parameter never changes its value when a procedure or function runs, you should consider using a constant parameter instead of a value parameter. Make a formal parameter a constant parameter by typing the reserved word const before the name of the parameter in the parameter list.

Like a value parameter, a constant parameter can be any expression. Using a constant parameter instead of a value parameter, however, protects you from accidentally assigning a new value to the parameter when you don't want the parameter to change. If you try to assign a value to a constant parameter, you'll receive an Invalid Variable Reference message because the value of a constant isn't allowed to change.

Deciding which kind of parameter to use

Keep these points in mind when you use parameters:

Use a value parameter when you don't want the value of the actual parameter to change. Because the formal parameter is only a copy of the actual parameter, the value of the actual parameter never changes.

(Remember, the actual parameter is the parameter specified in the procedure or function call. The formal parameter is the parameter specified in the called routine.)

Use a variable parameter when you do want the called routine to change the value of the passed parameter. Because both the actual and the formal parameters refer to the same value, the value of an actual variable parameter can change when it is processed in the called routine.

Use a constant parameter as the formal parameter in the called routine when the routine never changes the formal parameter. This protects you from inadvertently assigning a new value to the formal parameter when you really don't want the formal parameter to change.


Defining new data types

Section topics	See also

The Object Pascal language comes with a number of predefined data types (see the table in "Declaring variables" for a list of them). You can use these data types to create new data types that meet the specific needs of your application. These topics briefly describe the primary kinds of data types you can create:

Enumerated types

Subrange types

Array types

Set types

Record types

Object types

Object Pascal has other user-defined data types that aren't discussed in these topics: file, pointer, and procedural types. For more information about these types, see the corresponding topics in online Help.


Enumerated types

Section topics	See also

An enumerated type declaration lists all the values the type can have. These are some examples of enumerated type declarations:

type

 TDays = (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);

 TPrimaryColor = (Red, Yellow, Blue);

 TDepartment = (Finance, Personnel, Engineering, Marketing, MIS);

 TDog = (Poodle, GoldenRetriever, Dachshund, NorwegianElkhound, Beagle);

These are some variables of those enumerated types:

var

 DayOfWeek: TDays;

 Hue: TPrimaryColor;

 Department: TDepartment;

 DogBreed: TDog;

Each value listed in parentheses in an enumerated type declaration has an underlying integer value determined by the position of the value in the list. For example, Monday in the TDays type declaration has the value 0, Tuesday has the value 1, and so on. You could achieve the same result by declaring the DayOfWeek variable to be of type Integer and then assign an Integer value to represent each day of the week. While this system might work in an orderly and predictable series such as days of the week or months of the year, it's less useful when the order of represented values is arbitrary. Most people are not very good at remembering what a number stands for. For example, the assignment

DogBreed := Dachshund;

means much more to people than

DogBreed := 2;

When you list a value in an enumerated type, you are declaring the value as an identifier. If the previous enumerated type declarations and variable declarations were in your application, you could not, therefore, declare a Finance variable, because a Finance identifier already exists.

To experiment using an enumerated type:

1.	Create this form containing four radio buttons (in a group box), a label, and a button:

2.	Change these properties using the Object Inspector:

Change the group box caption to Select a course.

Name the first radio button HistoryButton, and change the caption to History.

Name the second radio button LiteratureButton, and change the caption to Literature.

Name the third radio button BiologyButton, and change the caption to Biology.

Name the fourth radio button PsychologyButton, and change the caption to Psychology.

Change the caption of the button to Display Selected Course.

3.	Declare a TCourse type below the implementation reserved word and the resource directive ({$R *.DFM}), and declare a SelectedCourse variable of type TCourse:

type

 TCourse = (Nothing, History, Literature, Biology, Psychology);

var

 SelectedCourse: TCourse;

4.	Create an OnClick event handler for each radio button. When you have finished, your code should look like this:

...

implementation

{$R *.DFM}

type

 TCourse = (Nothing, History, Literature, Biology, Psychology);

var

 SelectedCourse: TCourse;

procedure TForm1.HistoryButtonClick(Sender: TObject);

begin

 SelectedCourse := History;

end;

procedure TForm1.LiteratureButtonClick(Sender: TObject);

begin

 SelectedCourse := Literature;

end;

procedure TForm1.BiologyButtonClick(Sender: TObject);

begin

 SelectedCourse := Biology;

end;

procedure TForm1.PsychologyButtonClick(Sender: TObject);

begin

 SelectedCourse := Psychology;

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

case SelectedCourse of

  History: Label1.Caption := 'You are taking history';

  Literature: Label1.Caption := 'You are taking literature';

  Biology: Label1.Caption := 'You are taking biology';

  Psychology: Label1.Caption := 'You are taking psychology';

 else

    Label1.Caption := 'You are taking nothing';

end;

end;

...

5.	Run the application.

When you select a radio button to choose a course, an enumerated value is assigned to the SelectedCourse variable. The OnClick event handler for the button uses a case statement to display an appropriate message, based on the value of the SelectedCourse variable.


Subrange types

Section topics	See also

A subrange type is a range of values of any of these types: integer, Boolean, Char, or enumerated types. Subranges are useful when you want to limit the number of values a variable can have. To create a subrange, specify the minimum and maximum values in the range with two periods in between them. For example:

1..100

Declare the subrange as a new type in a type declaration. Here are some examples:

type

 TCompassRange  = 0..360;

 TValidLetter   = 'A'..'F';

 TMonthlyIncome = 10000..30000;

 THours         = 0..23;

 TPrecipitation = (Drizzle, Showers, Rain, Downpour, Thunderstorm);	{an enumerated type}

 TRain          = Drizzle..Downpour;	{a subrange of TPrecipitation}

Here is a simple application that uses a new subrange type:

This event handler is attached to the button's OnClick event:

procedure TForm1.Button1Click(Sender: TObject);

{$R+}

type

 TValidEntry = 1..100;

var

 Entry: TValidEntry;

begin

 Entry := StrToInt(Edit1.Text);

 Label2.Caption := 'Excellent!';

end;

When you run the application, enter a number greater than 100 in the edit box, and click the button, you'll get a run-time error that informs you a range-check error has occurred. If you enter a number within 1 to 100, the application runs as expected.

Note the use of the $R directive. {$R+} turns on range checking while the event handler runs. If you omit the $R directive, the compiler won't catch out-of-range errors. To read about compiler directives, see "Compiler directives." You can also turn on range checking by choosing Project|Options|Compiler Options and selecting Range Checking.


Array types

Section topics	See also

An array is an ordered collection of a data type, with each element of the collection specified by its numeric position within the collection. When the array is created, the elements don't contain useful values at first, but you can fill them with data and manipulate that data as you need to. For example, here is a variable declaration for an array of Double types:

var

 Check : array{1..10] of Double;

This declaration tells Delphi that the variable Check refers to a list of ten variables of type Double, each with a number (called its index) from 1 to 10.

Each item of an array is referred to by the name of the array, followed by its index enclosed in brackets ([ ]). So the array Check contains the ten variables Check[1], Check[2], Check[3], Check[4], and so on, up to Check[10]. You can use any of these variables wherever you would use a regular Real variable. Also, the index value doesn't have to be a constant. It can be any expression that yields an integer in the range 1 to 10. For example,

J := 5;

Check[J] := 0.0;

These statements assign the value 0.0 to variable Check[5]. (Because Check is an array of type Double, the value you assign must be Real and contain a decimal point. Also, all Double values must begin with a digit, not a decimal point, so you can't write 0.0 as .0)

If you want to assign a value of zero to all the Check variables in the array, you could use a for loop to do it. Because an index can be a variable, a for loop is easier to use than assigning a value to each item in an array with separate assignment statements. This for loop assigns the 0.0 amount to all ten checks:

for J := 1 to 10 do

 Check(J) := 0.0;

You can define arrays as types. For example,

type

 TCheck = array[1..100] of Double;

You can then declare variables of the array type. For example, this code declares a CheckingAccount to be a variable of type TCheck, which is an array of 100 real numbers.

var

 CheckingAccount: TCheck;

Multidimensional arrays

The arrays discussed so far are one-dimensional lists. Arrays can have many dimensions. Two-dimensional arrays can be used to hold all the values in a table, for example.

This is how you would create a two-dimensional array that can hold all the values in a table that contains 20 columns and 20 rows:

type

 TTable = array[1..20, 1..20] of Double;

Then you can declare a variable of the TTable type:

var

 BigTable: TTable;

To initialize all the values in the table to 0.0, you could use nested for loops:

var

 Col, Row: Integer;

...

for Col := 1 to 20 do

 for Row:= 1 to 20 do

   BigTable[Col, Row] := 0.0

String types: Arrays of characters

Strings, such as the text you enter for the caption on a control, are actually one-dimensional arrays of characters (an array of type Char). Object Pascal has two kinds of strings: long strings and short strings.

Long strings can contain a huge number of characters, so they expand and contract dynamically, depending on how many characters you assign to them. You declare long-string variables using the reserved word string.

Short strings can contain up to 255 characters. When you declare a short-string variable, you specify the size of the string, much as you specify the size of an array when you declare an array type. Short strings are useful in data structures where you need a fixed-size component.

These are some string type declarations:

type

 BigString: string;	{ long string }

 MyString: string[15];	{ short string }

 LittleString: string[1];	{ very short string }

These declarations declare the MyString type to contain up to 15 characters and the LittleString type to contain 1 character. If no size is specified, the string is a long string, as is the case with the BigString type.

You can then declare variables of these character arrays just as you would with any other data type:

var

 MyName: MyString;

 Letter, Digit: LittleString;

You can assign a value to a string variable or property by enclosing the sequence of characters that make up the string in single quotation marks or apostrophes ('). For example,

MyName := 'Frank P. Land';

Label1.Caption := 'File name';

Because MyName is a variable of type MyString, which holds 15 characters, the MyName variable is stored in memory as if the string were 'Frank P. Land  '. If you assigned the string 'Frank P. Borland' to the MyName variable, MyName would contain the string 'Frank P. Borlan', because the MyName variable can hold only 15 characters.

Object Pascal short strings are stored as arrays of characters in memory, but the arrays are actually one byte bigger than size of the specified string. The first position of the string, [0], is a byte that contains the size of the string.

Because strings are arrays of characters, you can access characters within a string with an index value, just as you would in an array. For example,

MyName[1]

refers to the first letter contained in the MyName string variable.

You can use Object Pascal's rich assortment of operators, procedures, and functions to manipulate variables and properties of string types. Here are some examples of string-handling routines in the run-time library:

Routine	Description
Concat	Concatenates multiple strings, creating a larger string. You can also concatenate strings together using the plus sign (+).
Copy	Returns a substring within a string.
Delete	Deletes a specified number of characters from a string beginning at specified position within the string.
Insert	Inserts another string within a string.
Length	Returns the length of the string.
Pos	Returns the position (index) of a substring within a string.

You can find information about using these string procedures and functions in online Help.

Finding the length of a string

This is a simple example that finds the length of a string the user enters in an edit box.

Example: finding string length

This is the button's event handler:

procedure TForm1.FindLengthClick(Sender: TObject);

type

 TEditString = string;

var

 UserString: TEditString;

begin

 UserString := Edit1.Text;

 Label2.Caption := 'This string is ' + IntToStr(Length(UserString)) +

   ' characters in length';

end;

When you run the application and click the button, the length of the string you enter in the edit box is reported in the Label2 control.

The text you enter in the edit box is assigned to the UserString variable:

UserString := Edit1.Text;

Because UserString is of type TEditString, it can contain as many as 255 characters.

The Length function returns the number of characters in UserString:

Length(UserString)

Because Length returns an Integer value, it needs to be converted to a string value. The IntToStr function does this:

IntToStr(Length(UserString))

The string that is assigned to Label2.Caption is a string made up of three substrings:

'This string is '

IntToStr(Length(UserString))

' characters in length'

These substrings are concatenated with the + operator into one string.

The previous example declares a string type and uses a variable of that type. You could write the event handler more concisely:

procedure TForm1.FindLengthClick(Sender: TObject);

begin

 Label2.Caption := 'This string is ' + IntToStr(Length(Edit1.Text)) +
    ' characters in length';

end;


Set types

Section topics	See also

A set is a collection of elements of one type. That one type must be either an integer, Boolean, Char, enumerated, or subrange type. Sets are useful for checking if a value belongs to a particular set.

Here is a simple application that uses a new set type:

This is the event handler for the button's OnClick event:

procedure TForm1.Button1Click(Sender: TObject);

type

 TVowels = set of Char;

var

 Vowels: TVowels;

begin

Vowels := ['A','E','I','O','U'];

if Edit1.Text[1] in Vowels then

   Label2.Caption := 'You are clever'

else

   Label2.Caption := 'Please try again';

end;

The new type TVowels is a set of type Char. Vowels is declared as a variable of type TVowels. The first line of code after the begin reserved word builds the vowel set (puts values into it) and assigns this new set to the Vowels variable. If the Vowels assignment looked like this,

Vowels := [];

then the Vowels set would contain no values and would be called an empty set.

The if statement uses the in operator to see if the value you enter into the edit box is in the Vowels set. The expression Edit1.Text[1] in Vowels is a Boolean expression that evaluates to either True or False. It tests if the first character in the Text string is in the Vowels set.


Record types

Section topics	See also

Records are collections of data that your application can refer to as a whole. For example, your application could use a TEmployee record type that is declared like this:

type

 TEmployee = record

   LastName : string[20];

   FirstName: string[15];

   YearHired: 1990..2000;

   Salary: Double;

   Position: string[20]

 end;

Records contain fields that hold data values. Each field has a data type. The fields of a TEmployee type are LastName, FirstName, YearHired, Salary, and Position. You can access these fields individually, or you can refer to the record as a whole.

For example, here are the declarations of two record variables:

var

 NewEmployee, PromotedEmployee: TEmployee;

Your code can refer to the Salary field of a NewEmployee record, like this:

NewEmployee.Salary := 43500.00

Or your code can manipulate the record as a single entity:

PromotedEmployee := NewEmployee;

When you refer to a field within a record, you need to type the name of the record, a period (.), and the name of the field:

PromotedEmployee.Position

If you are going to assign values to fields within a record, you can use the with statement. Instead of writing this code,

PromotedEmployee.LastName := 'Jeffries';

PromotedEmployee.FirstName := 'Richard';

PromotedEmployee.YearHired := 1990;

PromotedEmployee.Salary := 100000.00;

PromotedEmployee.Position := 'Senior technical writer';

you could assign the values like this:

with PromotedEmployee do

 begin

   LastName := 'Jeffries';

   FirstName := 'Richard';

   YearHired := 1990;

   Salary := 100000;

   Position := 'Senior technical writer';

 end;


A few words about objects

Section topics	See also

Objects are user-defined data types that are very important in programming for Delphi. Like records, objects are structured data types that contain fields that hold values. Objects, however, also contain methods. Methods are procedures and functions that act on the data in the object's fields.

Each time you time you add a new component to a form, you are adding a field to a form object. Each component is also an object, and each time you create an event handler so the component can respond to an event, you are automatically creating a method within the form object. All this happens automatically when you build the user interface for your application and create event handlers using the Object Inspector. Nevertheless, it is helpful to understand how Delphi uses objects to build your application. You can read more about objects in "Programming with Delphi objects."


Understanding Object Pascal units

Section topics	See also

These topics explain what a unit is, how you use it, and how to write your own units that aren't associated with forms.


What is a unit?

Section topics	See also

A unit is a collection of constants, data types, variables, and procedures and functions that can be shared by several applications. Delphi comes with a large number of predefined units you use to construct your Delphi applications. The Delphi Visual Component Library is made up of several units that declare the objects, components, and controls you use to design the interface for your application. Each time you add a check box to a form, for example, you are using the StdCtrls unit automatically because the TCheckBox component is declared in StdCtrls.

As you design the forms for your application, you are automatically creating new units that are associated with the forms. Units don't have to be associated with forms, however. You can use a predefined unit that contains only mathematical routines, for example. Or you can write your own unit of math functions.

All the declarations in a unit are related to one another. For example, the Dialogs unit contains all the declarations for using common dialog boxes in your applications.


The structure of a unit

Section topics	See also

Regardless of whether a unit is associated with a form, the basic structure of a unit is the same. The parts of a unit are

unit <identifier>;

interface

uses <list of units>;	{ optional }

{ public delcarations }

implementation

uses <list of units>;	{ optional }

{ private declarations }

{ implementation of procedures and functions }

initialization	{ optional }

{ optional initialization code}

end;

The unit header starts with the reserved word unit, followed by the unit's name (an identifier). The next item in a unit is the reserved word interface, which indicates the beginning of the interface part of the unit--the part visible to other units or applications that use this unit.

A unit can use the declarations in other units by specifying those units in a uses clause. The uses clause can appear in two places:

It can appear immediately after the reserved word interface.

Any code in the current unit can use the declarations declared in the interface parts of the units specified in the uses clause.

For example, suppose you are writing code in unit A, and you want to call a procedure declared in the interface part of unit B. Once you add the name of unit B to the uses clause in the interface part of unit A, any code in unit A can call the procedure declared in unit B.

If unit B is in the uses clause in the interface part of unit A, unit A cannot be in the uses clause in the interface part of unit B. This creates a circular unit reference, and when you attempt to compile, Delphi generates an error message.

It can appear immediately after the reserved word implementation.

Only declarations in the current unit can use the declarations in the interface parts of the units specified here. Any other units that use the current unit won't be able to use the units listed in this uses clause.

For example, if unit C is in a uses clause in the implementation part of unit B, and unit B is in a uses clause of unit A, unit A can use only the declarations in the interface parts of unit B. Unit A can't access the declarations in unit C, unless unit C is in a uses clause of unit A.

If unit B is in the uses clause in the implementation part of unit A, unit A can be in the uses clause in the implementation part of unit B. While circular unit references are not allowed in the interface part, they are fine in the implementation part.

Interface part

The interface part of a unit begins with the reserved word interface, which appears after the unit header and ends at the reserved word implementation. The interface determines what is "visible" (accessible) to any application or other unit using this unit.

In the unit interface part, you can declare constants, data types, variables, procedures, and functions. If Delphi builds a unit for you as you design a form, the form data type, the form variable that creates an instance of the form, and the event handlers are declared in the interface part. You can see examples of these in "Working with event handlers."

The procedures and functions visible to any unit or application using the unit are declared in the interface part. Their actual bodies--their implementations--are found in the implementation part. You don't need to use forward declarations, and they aren't allowed in the interface part. The interface part lists all the procedure and function headers; the implementation part contains the coded logic of the procedures and functions, including those that are event handlers.

An optional uses clause can appear in the interface and must immediately follow the interface reserved word.

Implementation part

The implementation part of a unit begins with the reserved word implementation. Everything declared in the interface part is accessible to the code in the implementation part.

The implementation can have additional declarations of its own, although these declarations aren't accessible to any other application or unit. These declarations are used by the procedures, functions, and event handlers declared in this unit.

An optional uses clause can appear in the implementation part and must immediately follow the implementation reserved word.

The bodies of the routines declared in the interface part must appear in the implementation part. The procedure or function header that appears in the implementation part can be identical to the declaration that appears in the interface part, or it can be in the short form.

To use the short form,
Type the procedure or function reserved word and follow it with the routine's name. You can omit any list of parameters, and, if the routine is a function, the return type.

For example, suppose you declare this function in the interface part:

function CalculateInterest(Principal, InterestRate: Double): Double;

When you write the body of the function in the implementation part, you can choose to write the function like this:

function CalculateInterest;

begin

 Result := Principal * InterestRate;

end;

Note that the parameter and the return type have been omitted.

If you declare any routines in the implementation part (not in the interface part), you must use the long form of the procedure or function header.

Initialization part

If you want to initialize any data the unit uses or makes available through the interface part to the application or unit using it, you can add an initialization part to the unit. Above the final end reserved word at the bottom of the unit, add the reserved word initialization. Between the initialization and end reserved words, you write the code that initializes the data. For example,

initialization

 { Your initialization code goes here }

end.

When an application uses a unit, the code within the unit's initialization part is called before the any other application code runs. If the application uses more than one unit, each unit's initialization part is called before the rest of the application runs.

Finalization part

If your unit needs to perform any cleanup when the application terminates, such as freeing any resources allocated in the initialization part, you can add a finalization part to your unit. The finalization part comes after the initialization part, but before the final end. For example,

initialization

 { Your initialization code goes here }

finalization

 { Your finalization code goes here }

end.

When an application terminates, it executes the finalization sections of its units in the opposite order it initialized them.


How units are used

Section topics	See also

To use another unit, you need to add the name of that unit to the uses clause of your current unit. Delphi does this automatically when you add new components to a form you are designing. If you add a component declared in a unit that isn't already in the standard Delphi uses clause, Delphi adds the unit name to the uses clause for you.

For example, if you add a color grid to a form, Delphi attaches ColorGrd to the end of the unit's uses clause. This makes all the declarations in the ColorGrd unit available to the unit associated with the form.

A few objects declared in units have no corresponding visual components in the Delphi environment, however. For example, if you want to use predefined message boxes in the code within a unit, you must add the MsgDlg unit to the uses clause yourself. The same is true if you want to use the TPrinter object in the Printers unit.

To add a unit name to the uses clause,
Put the unit name before the semicolon (;) that ends the uses clause, separating the unit name from the previous unit name in the clause with a comma.

To use a routine declared in another unit,
Preface the name of the routine with the name of the unit in which it is declared.

For example, to call a ComputePayment function declared in Unit1 from Unit2, make the call like this:

Amount := Unit1.ComputePayment(1000, .085, 242);

You can preface any identifier--property, constant, data type, variable, or routine--with the unit name.

The uses clause in the implementation part

Object Pascal allows you to put an optional uses clause in a unit's implementation part. If you put it in, it must appear immediately following the implementation reserved word.

A uses clause in the implementation part lets you hide the inner details of a unit, because units specified in the implementation part aren't visible (accessible) to the users of the unit.


Writing a unit from scratch

Section topics	See also

You are free to add code to any unit that Delphi creates. You must not alter the code that Delphi generates, however.

If you want to create a new unit in your project that is not associated with a form, Delphi gets you started.

To create a new unit,
Choose File|New Unit.

A new unit is added to your current project. The source code of the new unit looks like this:

unit Unit2;

interface

implementation

end.

The name Delphi selects for your unit depends on how many units are already in your project. You can then fill in this skeleton unit to include the code that you want.

Note that the end reserved word is not paired with a begin reserved word. If you like, you can add an initialization reserved word above the find end reserved word to create an initialization part. Many of the units you write don't require any initialization code, as they are often collections of related procedures, functions, and data types. If you do want to include initialization code, add the initialization reserved word and put the initializing code between between initialization and end.

When you choose to compile or build your project, the new units you added are compiled into files with .DCU file extensions. When the MYUNIT.PAS file is compiled, for example, the resulting file is MYUNIT.DCU. This newly created file is machine code that is linked into your project executable file.


Adding an existing unit to a project

Section topics	See also

If you want to use a unit that is already complete, whether it is a unit Delphi created that is associated with a form, or whether you or someone else wrote the unit from scratch, you can simply add it to your project.

To add an existing unit to your project:

1.	Open the project you want to add the unit to.

2.	Choose File|Open File, select the source code (the .PAS file) of the unit you want added, and choose OK.


See also

Exceptions and the flow of execution

Protecting blocks of code

Object scope

Programming with Delphi objects


Programming with Delphi objects

Section topics	See also

Object-oriented programming (OOP) is a natural extension of structured programming. OOP requires that you use good programming practices and makes it very easy for you to do so. The result is clean code that is easy to extend and simple to maintain.

Once you create an object for an application, you and other programmers can then use that same object in other applications. Reusing objects can greatly cut your development time and increase productivity for yourself and others.

Together, Delphi and the Object Pascal language make programming with objects easier than ever before. These topics explain what you need to know about objects to use Delphi effectively. If you want to create new components and put them on the Delphi Component palette, see the the Developer's Guide.

These are the primary topics described:

What is an object?

Inheriting data and code from an object

Object scope

Assigning values to object variables

Creating nonvisual objects

Before reading these topics, you should know how to design a user interface for your application using the Delphi tools, how a Delphi project is structured, and how to handle events. You should also know the fundamentals of the Object Pascal language. To review topics in any of these areas, refer to the appropriate Help topics.


What is an object?

Section topics	See also

An object is a data type that wraps up data and code all into one bundle. Before OOP, code and data were treated as separate elements.

Think of the work involved to assemble a bicycle if you have all the bicycle parts and a list of instructions for the assembly process. This is analogous to writing a Windows program from scratch without using objects. Delphi gives you a head start on "building your bicycle" because it already gives you many of the "preassembled bicycle parts"--the Delphi forms and components.

You can begin to understand what an Object Pascal object is if you understand what an Object Pascal record is. In "Writing Object Pascal code," you read that records are made of up fields that contain data, and each of these fields has its own data type. Records make it easy to refer to a related collection of varied data elements as one entity.

Objects are also collections of data elements. Like records, they also have fields, each of which has its own data type.

Unlike records, objects also contain code--procedures and functions--that act on the data contained in the object's fields. These procedures and functions are called methods.

Also unlike records, objects can contain properties. The properties of Delphi objects have default values. You can change these values at design time to modify an object to suit the needs of your project without writing code. If you want a property value to change at runtime, you need to write only a very small amount of code.


Examining a Delphi object

Section topics	See also

When you choose to create a new project, Delphi displays a new form for you to customize. In the Code editor, Delphi declares a new object type for the form and produces the code that creates the new form object. A later section discusses why a new object type is declared for each new form. For now, examine the following example to see what the code in the Code editor looks like:

unit Unit1;

interface

uses Windows, Classes, Graphics, Forms, Controls, Apps;

type

 TForm1 = class(TForm)	{ The type declaration of the form begins here }

 private

   { Private declarations }

 public

   { Public declarations }

 end;	{ The type declaration of the form ends here }

var

 Form1: TForm1;

implementation	{ Beginning of implementation part }

{$R *.DFM}

end.	{ End of of implemntation part and unit}

The new object type is TForm1, and it is derived from type TForm, which is also an object. A later section presents more about TForm and objects derived from other objects.

Recall that an object is like a record in that they both contain data fields, but an object also contains methods--code that acts on the object's data. So far, type TForm1 appears to contain no fields or methods, because you haven't added to the form any components (the fields of the new object), and you haven't created any event handlers (the methods of the new object). As discussed later, TForm1 does contain fields and methods, even though you don't see them in the type declaration.

This variable declaration declares a variable named Form1 of the new object type TForm1.

var

 Form1: TForm1;

Form1 is called an instance of the type TForm1. The Form1 variable refers to the form itself to which you add components to design your user interface.

You can declare more than one instance of an object type. You might want to do this to manage multiple child windows in a Multiple Document Interface (MDI) application, for example. Each instance carries its own data in its own package, and all the instances of an object use the same code.

Although you haven't added any components to the form or written any code, you already have a complete Delphi application that you can compile and run. All it does is display a blank form because the form object doesn't yet contain the data fields or methods to do more.

Suppose, though, that you add a button component to this form and an OnClick event handler for the button, that changes the color of the form when the user clicks the button. You then have an application that's about as simple as it can be and still actually do something. Here is the form for the application:

When the user clicks the button, the form changes color to green. This is the event-handler code for the button's OnClick event:

procedure TForm1.Button1Click(Sender: TObject);

begin

 Form1.Color := clGreen;

end;

If you create this application and then look at the code in the Code editor, this is what you see:

unit Unit1;

interface

uses Windows, Classes, Graphics, Forms, Controls, Apps;

type

 TForm1 = class(TForm)

   Button1: TButton;	{ New data field }

   procedure Button1Click(Sender: TObject);	{ New method declaration }

 private

   { Private declarations }

 public

   { Public declarations }

 end;

var

 Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);	{ The code of the new method }

begin

 Form1.Color := clGreen;

end;

end.

The new TForm1 object now has a Button1 field--that's the button you added to the form. TButton is an object type, so Button1 is also an object. Object types, such as TForm1, can contain other objects, such as Button1, as data fields. Each time you put a new component on a form, a new field with the component's name appears in the form's type declaration.

All the event handlers you write in Delphi are methods of the form object. Each time you create an event handler, a method is declared in the form object type. The TForm1 type now contains a new method, the Button1Click procedure, declared within the TForm1 type declaration.

The actual code for the Button1Click method appears in the implementation part of the unit. The method is the same as the empty event handler you created with Delphi and then filled in to respond when the user clicks the button as the application runs.

Changing the name of a component

You should always use the Object Inspector to change the name of a component. For example, when you change the default name of a form from Form1 to something else by changing the value of the Name property using the Object Inspector, the name change is reflected throughout the code Delphi produces. If you wrote the previous application, but named the form ColorBox, this is how your code would appear:

unit Unit1;

interface

uses Windows, Classes, Graphics, Forms, Controls, Apps;

type

 TColorBox = class(TForm)	{ Changed from TForm1 to TColorBox }

   Button1: TButton;

   procedure Button1Click(Sender: TObject);

 private

   { Private declarations }

 public

   { Public declarations }

 end;

var

 ColorBox: TColorBox;	{ Changed from Form1 to ColorBox }

implementation

{$R *.DFM}

procedure TColorBox.Button1Click(Sender: TObject);

begin

 Form1.Color := clGreen;	{ The reference to Form1 didn't change! }

end;

end.

Notice that the name of the form object type changed from TForm1 to TColorBox. Also note that the form variable is now ColorBox, the name you gave to the form. References to the ColorBox variable and the TColorBox type also appear in the final begin and end block of code, the code that creates the form object and starts the application running. If Delphi originally generated the code, it updates it automatically when you use the Object Inspector to change the name of the form or any other component.

Note that the code you wrote in the OnClick event handler for the button hasn't changed. Because you wrote the code, you have to update it yourself and correct any references to the form if you change the form's name. If you don't, your code won't be compiled. In this case, change the code to this:

procedure TColorBox.Button1Click(Sender: TObject);

begin

 ColorBox.Color := clGreen;

end;

You should change the name of a component only with the Object Inspector. While nothing prevents you from altering the code Delphi produced and changing the name of component variables, you won't be able to compile your program.


Inheriting data and code from an object

Section topics	See also

The TForm1 object described in the topic "Examining a Delphi object" might seem quite simple. If you create the application, TForm1 appears to contain one field, Button1, one method, Button1Click, and no properties. Yet you can change the size of the form, add or delete minimize and maximize buttons, or set up the form to become part of a Multiple Document Interface (MDI) application. How is it possible to do all these things with a form that contains only one field and one method?

The answer lies in the notion of inheritance. Recalling the bicycle analogy, once you have put together all the objects that make up a complete "bicycle object," you can ride it because it has all the essentials to make the bicycle useful--it has pedals you push to make the wheels go around, it has a seat for you to sit on, it has handlebars so you can steer, and so on. Similarly, when you add a new form to your project, it has all the capabilities of any form. For example, all forms provide a space to put other components on them, all forms have the methods to open, show, and hide themselves, and so on.

Suppose, though, that you want to customize that bicycle, just as you would customize a form object in Delphi. You might adjust a few gear settings, add a headlight, and provide a horn with a selection of sounds--just as you might customize a form by adding or rearranging buttons, changing a few property values, and adding a new method that allows the form to appear with a plaid background.

To change the bicycle to make it exactly as you want it, you start with the basic model and then customize it. You do the same thing with Delphi forms. When you add a new form to your project, you've added the "basic model" form. By adding components to the form, changing property values, and writing event handlers, you are customizing the new form.

To customize any object, whether it be a blank form, a form with multiple controls that is used as a dialog box, or a new version of the Delphi bitmap button, you start by deriving a new object from an existing object. When you add a new form to your project, Delphi automatically derives a new form object for you from the TForm type.

At the moment a new form is added to a project, the new form object is identical to the TForm type. Once you add components to it, change properties, and write event handlers, the new form object and the TForm type are no longer the same.

No matter how you customize your bicycle, it can still do all the things you expect a bicycle to do. Likewise, a customized form object still exhibits all the built-in capabilities of a form, and it can still do all the things you expect a form to do, such as change color, resize, close, and so on. That's because the new form object inherits all the data fields, properties, methods, and events from the TForm type.

When you add a new form to an Delphi project, Delphi creates a new object type, TForm1, deriving it from the more generic object, TForm. The first line of the TForm1 type declaration specifies that TForm1 is derived from TForm:

TForm1 = class(TForm)

Because TForm1 is derived from TForm, all the elements that belong to a TForm type automatically become part of the TForm1 type. If you look up TForm in online Help (you can click the form itself and press F1), you see lists of all the properties, methods, and events in the TForm object type. You can use all the elements inherited from TForm in your application. Only the elements you specifically add to your TForm1 type, such as components you place on the form or event handlers (methods) you write to respond to events, appear in the TForm1 type declaration. These are the things that make TForm1 different from TForm.

The more broad-based or generic object from which another more customized object inherits data and code is called the ancestor of the customized object. The customized object itself is a descendant of its ancestor. An object can have only one immediate ancestor, but it can have many descendants. For example, TForm is the ancestor type of TForm1, and TForm1 is a descendant of TForm. All form objects are descendants of TForm, and you can derive many form objects from TForm.


Objects, components, and controls

Section topics	See also

When you look up TForm in online Help, you'll notice that TForm is called a component. Don't let this confuse you. All components and controls are also objects. The terminology used in the documentation comes from the inheritance hierarchy of the Delphi Visual Component Library. This is a greatly simplified diagram of the hierarchy, omitting some intermediary objects:

Everything in this hierarchy is an object. Components, which inherit data and code from a TObject type, are objects with additional properties, methods, and events that make them suitable for specialized purposes, such as the ability to save their state to a file. Controls, which inherit data and code from a TComponent type (which in turn inherits elements from TObject) have additional specialized capabilities, such as the ability to display something. So controls are components and objects, components are objects but not necessarily controls, and objects are simply objects. These topics refer to all components and controls as objects.

Even though TCheckBox isn't an immediate descendant of TObject, it still has all the attributes of any object because TCheckBox is ultimately derived from TObject in the VCL hierarchy. TCheckBox is a very specialized kind of object that inherits all the functionality of TObject, TComponent, and TControl, and defines some unique capabilities of its own.


Object scope

Section topics	See also

An object's scope determines the availability and accessibility of the data fields, properties, and methods within that object. Using the earlier bicycle analogy, if you were to add a headlight only to your customized "bicycle object," the headlight would belong to that bicycle and to no other. If, however, the "basic model bicycle object" included a headlight, then all bicycle objects would inherit the presence of a headlight. The headlight could lie either within the scope of the ancestor bicycle object--in which case, a headlight would be a part of all descendant bicycle objects--or within the scope only of the customized bicycle object, and available only to that bicycle.

Likewise, all data fields, properties, and methods declared within an object declaration are within the scope of the object, and are available to that object and its descendants. Even though the code that makes up the methods appears outside of the object declaration in the implementation part of the unit, those methods are still within the scope of the object because they were declared within the object's declaration.

When you write code in an event handler of an object that refers to properties, methods, or fields of the object itself, you don't need to preface these identifiers with the name of the object variable. For example, if you put a button and an edit box on a new form, you could write this event handler for the OnClick event of the button:

procedure TForm1.Button1Click(Sender: TObject);

begin

 Color := clFuchsia;

 Edit1.Color := clLime;

end;

The first statement colors the form. You could have written the statement like this:

Form1.Color := clFuchsia

It's not necessary, however, to put the Form1 qualifier on the Color property because the Button1Click method is within the scope of the TForm1 object. Any time you are within an object's scope, you can omit the qualifier on all properties, methods, and fields that are part of the object.

The second statement refers to the Color property of a TEdit object. Because you want to access the Color property of the TEdit1 type, not of the TForm1 type, you need to specify the scope of the Color property by including the name of the edit box, so the compiler can determine which Color property you are referring to. If you omit it, the second statement is like the first; the form ends up lime green, and the edit box control remains unchanged when the handler runs.

Because it's necessary to specify the name of the edit box whose Color property you are changing, you might wonder why it's not necessary to specify the name of the form as well. This is unnecessary because the control Edit1 is within the scope of the TForm1 object; it's declared to be a data field of TForm1.


Accessing components on another form

Section topics	See also

If Edit1 were on some other form, you would need to preface the name of the edit box with the name of the form object variable. For example, if Edit1 were on Form2, it would be a data field in the TForm2 object declaration, and would lie with the scope of Form2. You would write the statement to change the color of the edit box in Form2 from the TForm1.ButtonClick method like this:

Form2.Edit1.Color := clLime;

In the same way, you can also access methods of a component on another form. For example,

Form2.Edit1.Clear;

To give the code of Form1 access to properties, methods, and events of Form2, you need to add Unit2 to the uses clause of Unit1 (assuming the units associated with Form1 and Form2 are named Unit1 and Unit2, respectively). For more information about the uses clause, see "Understanding Object Pascal units."


Scope and descendants of an object

Section topics	See also

The scope of an object extends to all the object's descendants. That means all the data fields, properties, methods, and events that are part of TForm are within the scope of TForm1 also, because TForm1 is a descendant of TForm. Your application won't be able to declare a data field using the same name as a data field in the object's ancestor.

If Delphi displays a duplicate-identifier message, it's possible a data field with the same name already exists in the ancestor object, such as in TForm. If you see the duplicate identifier message and you don't understand why, try altering the name of the identifier slightly. It might be that you happened to choose the name of a data field in an ancestor object.

Overriding a method

You can, however, use the name of a method within an ancestor object to declare a method within a descendant object. This is how you override a method. You would most likely want to override an existing method if you want the method in the descendant object to do the same thing as the method in the ancestor object, but the task is accomplished in another way. In other words, the code that implements the two methods differs.

It's not often that you would want to override a method unless you are creating new components. You should be aware that you can do so, though, and that you won't receive any warning or error message from the compiler. You can read more about overriding methods in the Developer's Guide.


Public and private declarations

Section topics	See also

When you build an application using the Delphi environment, you are adding data fields and methods to a descendant of TForm. You can also add fields and methods to an object without putting components on a form or filling in event handlers, but by modifying the object type declaration directly.

You can add new data fields and methods to either the public or private part of an object. Public and private are standard directives in the Object Pascal language. Treat them as if they were reserved words. When you add a new form to the project, Delphi begins constructing the new form object. Each new object contains the public and private directives that mark locations for data fields and methods you want to add to the code directly. For example, note the private and public parts in this new form object declaration that so far contains no fields or methods:

type

 TForm1 = class(TForm)

 private

   { Private declarations }

 public

   { Public declarations }

 end;

Use the public part to

Declare data fields you want methods in objects in other units to access

Declare methods you want objects in other units to access

Declarations in the private part are restricted in their access. If you declare fields or methods to be private, they are unknown and inaccessible outside the unit the object is defined in. Use the private part to

Declare data fields you want only methods in the current unit to access

Declare methods you want only objects defined in the current unit to access

To add data fields or methods to a public or private section, put the fields or method declarations after the appropriate comment, or erase the comments before you add the code. Here is an example:

type

 TForm1 = class(TForm)

   Edit1: TEdit;

   Button1: TButton;

   procedure Button1Click(Sender: TObject);

 private

   { Private declarations }

   Number: Integer;

   function Calculate(X, Y: Integer): Integer;

 public

   { Public declarations }

   procedure ChangeColor;

 end;

Place the code that implements the Calculate and ChangeColor methods in the implementation part of the unit.

The Number data field and Calculate function are declared to be private. Only objects within the unit can use Number and Calculate. Usually, this restriction means that only the form object can use them, because each Delphi unit contains just one object type declaration of type TForm.

Because the ChangeColor method is declared to be public, code in other units can use it. Such a method call from another unit must include the object name in the call:

Form1.ChangeColor;

The unit making this method call must have Form1 in its uses clause.

Note:	When adding fields or methods to an object, always put the fields before the method declarations within each public or private part.


Accessing object fields and methods

Section topics	See also

When you want to change the value of a property of an object that is a field in the form object, or you want to call a method of an object that is a field in the form object, you must include the name of that object in the property name or method call. For example, if your form has an edit box control on it and you want to change the value of its Text property, you should write the assignment statement something like this, making sure to specify the name of the edit box (in this example, Edit1):

Edit1.Text := 'Frank Borland was here';

Likewise, if you want to clear the text selected in the edit box control, you would write the call to the ClearSelection method like this:

Edit1.ClearSelection;

If you want to change several property values or call several methods for an object that is a field in a form object, you can use the with statement to simplify your code. The with statement is as convenient to use with objects as it is with records. For example, this is an event handler that makes several changes to a list box when a user clicks the button on the form:

procedure TForm1.Button1Click(Sender: TObject);

begin

 ListBox1.Clear;

 ListBox1.MultiSelect := True;

 ListBox1.Items.Add('One');

 ListBox1.Items.Add('Two');

 ListBox1.Items.Add('Three');

 ListBox1.Sorted := True;

 ListBox1.Font.Style := [fsBold];

 ListBox1.Font.Color := clPurple;

 ListBox1.Font.Name := 'Times New Roman';

 ListBox1.ScaleBy(125, 100);

end;

If the code uses a with statement, it looks like this:

procedure TForm1.Button1Click(Sender: TObject);

begin

 with ListBox1 do

 begin

   Clear;

   MultiSelect := True;

   Items.Add('One');

   Items.Add('Two');

   Items.Add('Three');

   Sorted := True;

   Font.Style := [fsBold];

   Font.Color := clPurple;

   Font.Name := 'Times New Roman';

   ScaleBy(125, 100);

 end;

end;

By using the with statement, you don't have to qualify each reference to a ListBox1 property or method with the ListBox1 identifier. Within the with statement, all the properties and methods called are local in scope to the ListBox1 object variable.


Assigning values to object variables

Section topics	See also

You can assign one object variable to another object variable if the variables are of the same type or assignment compatible, just as you can assign variables of any type other than objects to variables of the same type or assignment-compatible types. For example, if objects TForm1 and TForm2 are derived from type TForm, and the variables Form1 and Form2 have been declared, you could assign Form1 to Form2:

Form2 := Form1;

You can also assign an object variable to another object variable if the type of the variable you are assigning a value to is an ancestor of the type of the variable being assigned. For example, here is a TDataForm type declaration and a variable declaration section declaring two variables, AForm and DataForm:

type

 TDataForm = class(TForm)

   Button1: TButton;

   Edit1: TEdit;

   DataGrid1: TDataGrid;

   Database1: TDatabase;

 private

   { Private declarations }

 public

   { Public declarations }

 end;

var

 AForm: TForm;

 DataForm: TDataForm;

AForm is of type TForm, and DataForm is of type TDataForm. Because TDataForm is a descendant of TForm, this assignment statement is legal:

AForm := DataForm;

You might wonder why this is important. Taking a look at what happens behind the scenes when your application calls an event handler shows you why.

Suppose you fill in an event handler for the OnClick event of a button. When the button is clicked, the event handler for the OnClick event is called.

Each event handler has a Sender parameter of type TObject. For example, note the Sender parameter in this empty Button1Click handler:

procedure TForm1.Button1Click(Sender: TObject);

begin

end;

If you look back at the figure of the simplified hierarchy diagram in the topic "Objects, components, and controls," you'll recall that TObject is at the top of the Delphi Visual Component Library. That means that all Delphi objects are descendants of TObject. Because Sender is of type TObject, any object can be assigned to Sender. Although you don't see the code that makes the assignment, the component or control to which the event happened is assigned to Sender. This means the value of Sender is always the control or component that responds to the event that occurred.

How is this information useful? You can test Sender to find the type of component or control that called the event handler using the reserved word is. For example,

if Sender is TEdit then

 DoSomething

else

 DoSomethingElse;

The drag-and-drop demonstration project shipped as part of the Delphi package has a project file named DRAGDROP.DPR. If you load the project and look at the DROPFONT.PAS unit code, you'll find that the Memo1DragOver method checks the type of an object variable. In this case, the parameter is Source rather than Sender:

procedure TForm1.Memo1DragOver(Sender, Source: TObject; X, Y: Integer;

 State: TDragState; var Accept: Boolean);

begin

 Accept := Source is TLabel;

end;

The Source parameter is also of type TObject. Source is assigned the object that is being dragged. The purpose of the Memo1DragOver method is to ensure that only labels can be dragged. Accept is a Boolean parameter, so it can have only one of two values, True or False. If Accept is True, the control the user has selected to drag can be dragged. If Accept is False, the user can't drag the selected control.

This expression is assigned to the Accept variable:

Source is TLabel

The is reserved word checks the Source variable to determine if Source is of type TLabel. If it is, the expression evaluates to True. Because the expression becomes True only when the user tries to drag a label, Accept becomes True only under the same circumstances.

The next event handler in the drag-and-drop demonstration, Memo1DragDrop, also uses the Source parameter. The purpose of this method is to change the font of the text in the memo control to the font of the label dropped on the memo control. Here is the method's implementation:

procedure TForm1.Memo1DragDrop(Sender, Source: TObject; X, Y: Integer);

begin

 Memo1.Font := (Source as TLabel).Font;

end;

When writing the assignment statement in this handler, the developer didn't know which label the user would drag. By referring to the name of the label as (Source as TLabel), the font of the label the user dropped is assigned to Memo1.Font because Source contains the name of the control the user dragged and dropped. Only if Source is a label does the event handler allow the assignment to occur.


Creating nonvisual objects

Section topics	See also

Most of the objects you use in Delphi are components you can see at both design time and runtime, such as edit boxes and string grids. A few, such as common dialog boxes, are components you don't see at design time, but they appear at runtime. Still others, such as timers and data source components, never have any visual representation in your application at runtime, but they are there for your application to use.

Occasionally you might want to create your own nonvisual objects for your application. For example, you might want to create a TEmployee object that contains Name, Title, and HourlyPayRate fields. You could then add a CalculatePay method that uses the data in the HourlyPayRate field to compute a paycheck amount for a given period.

The TEmployee type declaration could look like this:

type

 TEmployee = class(TObject)

   Name: string[25];

   Title: string[25];

   HourlyRate: Double;

   function CalculatePayAmount: Double;

  end;

In this case, TEmployee is derived from TObject. TEmployee contains three fields and one method. Because it is derived from TObject, TEmployee also contains all methods of TObject (TObject has no fields).

Place object type declarations you create without the help of Delphi in the type declaration part of your unit along with the form type declaration.

In the variable declaration part of the unit, you need to declare a variable of the new type:

var

 Employee: TEmployee;


Creating an instance of an object

Section topics	See also

TEmployee is just an object type. An actual object doesn't exist in memory until the object is instantiated, or created, by a constructor call. A constructor is a method that allocates memory for the new object and points to the new object, which is called an instance of the object type. Calling the Create method, the constructor, assigns this instance to a variable.

If you want to declare an instance of the TEmployee type, your code must call Create before you can access any of the fields of the object:

 Employee := TEmployee.Create;

In the TEmployee type declaration, there isn't a Create method, but because TEmployee is derived from TObject, and TObject has a Create method, TEmployee can call Create to create a TEmployee instance, which is assigned to the Employee variable.

Now you are ready to access the fields of an Employee object, just as you would any other Delphi object.


Destroying your object

Section topics	See also

When you are through using your object, you should destroy it, which releases the memory the object used. You destroy an object by calling a destructor, a method that releases the memory allocated to the object.

Delphi has two destructors you can use, Destroy and Free. You should almost always use Free. In its implementation, the Free method calls Destroy, but only when the instance pointer isn't nil; in other words, the pointer still points to the instance. For this reason, it is safer to call Free than Destroy. Also, calling Free is slightly more efficient in the resulting code size.

To destroy the Employee object when you have finished using it, you would make this call:

Employee.Free;

Just as it inherits the Create method, TEmployee inherits Free from TObject.

To follow good programming practices, you should place your destructor call in the finally part of a try..finally block, while placing the code that uses the object in the try part. This assures that the memory allocated for your object is released even if an exception occurs while your code is attempting to use the object. You can read about exceptions and what a try..finally block is in "Handling exceptions."

You can find a more common example of creating a nonvisual object in "Working with string lists." String list objects are commonly used in Delphi applications.


See also

Overview of components

Overview of component creation

Working with the Code editor


Using the integrated debugger

Section topics	See also

No matter how careful you are when you write code, a newly completed program is likely to contain errors, or bugs, that prevent it from running as it was designed to. Debugging is the process of locating and fixing the errors in your programs.

Delphi contains an integrated debugger, so you can debug programs without leaving the Delphi development environment. This section contains the following topics:

Types of bugs

Planning a debugging strategy

Starting a debugging session

Controlling program execution

Restarting the program

Using breakpoints

Examining program data values

Viewing function calls

Handling hardware and language exceptions

You can access the integrated debugger through either of two menus: Run or View.


Types of bugs

Section topics	See also

The integrated debugger can help find two basic types of programming errors: runtime errors and logic errors.


Runtime errors

Section topics	See also

If your program successfully compiles, but fails when you run it, you've encountered a runtime error. Your program contains valid statements, but the statements cause errors when they're executed. For example, your program might be trying to open a nonexistent file, or it might be trying to divide a number by zero. The operating system detects runtime errors and stops your program execution if such an error is encountered.

Without a debugger, runtime errors can be difficult to locate because the compiler doesn't tell you where the error is located in your source code. Often, the only clue you have to work with is where your program failed and the error message generated by the runtime error.

Although you can find runtime errors by searching through your program source code, the integrated debugger can help you quickly track down these types of errors. Using the integrated debugger, you can run to a specific program location. From there, you can begin executing your program one statement at a time, watching the behavior of your program with each step. When you execute the statement that causes your program to fail, you have pinpointed the error. From there, you can fix the source code, recompile the program, and resume testing your program.


Logic errors

Section topics	See also

Logic errors are errors in design and implementation of your program. Your program statements are valid (they do something), but the actions they perform are not the actions you had in mind when you wrote the code. For instance, logic errors can occur when variables contain incorrect values, when graphic images don't look right, or when the output of your program is incorrect.

Logic errors are often the most difficult type of errors to find because they can show up in places you might not expect. To be sure your program works as designed, you need to thoroughly test all of its aspects. Only by scrutinizing each portion of the user interface and output of your program can you be sure that its behavior corresponds to its design. As with runtime errors, the integrated debugger helps you locate logic errors by letting you monitor the values of your program variables and data objects as your program executes.


Planning a debugging strategy

Section topics	See also

After program design, program development consists of a continuous cycle of program coding and debugging. Only after you thoroughly test your program should you distribute it to your end users. To ensure that you test all aspects of your program, it's best to have a thorough plan for your debugging cycles.

One good debugging method involves breaking your program down into different sections that you can systematically debug. By closely monitoring the statements in each program section, you can verify that each area is performing as designed. If you do find a programming error, you can correct the problem in your source code, recompile the program, and then resume testing.


Starting a debugging session

Section topics	See also

If you find a program runtime or logic error, you can begin a debugging session by running your program under the control of the debugger.

To begin a debugging session:

1.	Generate debug information when you compile your program.

2.	Run your program from within Delphi.

When debugging, you have complete control of your program's execution. You can pause the program at any point to examine the values of program variables and data structures, to view the sequence of function calls, and to modify the values of program variables to see how different values affect the behavior of your program.

You can also use the debugger to watch each step of your program's execution. By stepping through your program's source code (as described in "Stepping through code") you can monitor your program code as it is executed.


Generating debugging information

Section topics	See also

Before you can begin a debugging session, you need to compile your project with symbolic debug information. Symbolic debug information, contained in a symbol table, enables the debugger to make connections between your program's source code and the machine code that's generated by the compiler. This way you can view the actual source code of your program while running the program through the debugger.

Delphi generates symbolic debug information by default.

To manually turn on symbolic debug information for your project:

1.	Choose Project| Options.

2.	Click the Compiler tab to access the debugging options.

3.	Check the Debug Information and Local Symbols check boxes.

When you generate symbolic debug information for use with the internal debugger, the compiler stores a symbol table in each associated .DCU file.

If you want to use Turbo Debugger (the standalone debugger), you also need to include symbolic debug information in your final .EXE file.

To include symbolic debug information in your project's compiled .EXE file:

1.	Choose Project|Options.

2.	Click the Linker tab to access the linker options.

3.	Check the Include TD32 Debug Info check box.

Note:	Be aware that including symbolic debug information in your executable file significantly increases the size of the final .EXE file. Because of this, you want to include debug information in your files only during the development stage of your project. Once your program is fully debugged, compile your program without debug information to reduce the final .EXE file size.

# DebuggerBeforeYouBegin Before you begin

In order to access the integrated debugger commands, you need to make sure the integrated debugger is enabled.

To enable the integrated debugger:

1.	Choose Tools|Environment Options and click the Preferences page tab.

2.	Check the Integrated Debugging check box, if necessary (this box is checked by default).

3.	If you want, check the Minimize On Run check box to specify that the Delphi environment be minimized when you run your program.


Running your program

Section topics	See also

Once you've compiled your program with debug information, you can begin a debugging session by running your program from Delphi. The debugger takes control whenever you run, trace, or step through your program code.

When you run your program under the control of the debugger, it behaves as it normally would; your program creates windows, accepts user input, calculates values, and displays output. In the time that your program is not running, the debugger has control, and you can use its features to examine the current state of the program. By viewing the values of variables, the functions on the call stack, and the program output, you can ensure that the area of code you're examining is performing as it was designed to.

As you run your program through the debugger, you can watch the behavior of your application in the windows it creates. For best results during your debugging sessions, arrange your screen so you can see both the Code editor and your application window as you debug. To keep windows from flickering as the focus alternates between the debugger windows and those of your application, arrange the windows so they don't overlap (tile the windows). With this setup, your program's execution will be quicker and smoother during the debugging session.

# DebuggerSpecifyingProgramArguments Specifying program arguments

If the program you want to debug uses command-line arguments, you can specify those arguments from within Delphi.

To specify command-line arguments for your application:

1.	Choose Run|Parameters.

2.	In the Run Parameters dialog box, type the arguments you want to pass to your program when you run it under the control of the integrated debugger.

3.	Choose OK.

# DebuggerDebuggingADLL Debugging a DLL

To specify an .EXE file to run for the purpose of debugging a .DLL project:

1.	Choose Run|Parameters.

2.	In the Host Application edit box, type the name of the .EXE file.

3.	Choose OK.


Controlling program execution

Section topics	See also

The most important property of a debugger is that it lets you control the execution of your program; you can control whether your program executes a single line of code, an entire function, or an entire program block. By dictating when the program should run and when it should pause, you can quickly move over the sections that you know work correctly and concentrate on the sections that are causing problems. The integrated debugger lets you control the execution of your program in the following ways:

Running to the cursor location

Stepping and tracing

Running to a breakpoint

Pausing your program

When running code through the debugger, all program execution is based on lines of source code. The integrated debugger lets you control the rate of debugging to the level of a single line of code. However, the debugger considers multiple program statements on one line of text to be a single line of code; you cannot individually debug multiple statements contained on a single line of text. In addition, the debugger regards a single statement that's spread over several lines of text as a single line of code.


Running to the cursor location

Section topics	See also

When beginning a debugging session, you often run your program to a spot just before the suspected location of the problem. At that point, you want to ensure that all data values are as they should be. If everything appears to be correct, you can run your program to another location, and again check to ensure things are functioning correctly.

To run to a specific program location:

1.	In the Code editor, position the cursor on the line of code where you want to begin (or resume) debugging.

2.	Run to the cursor location using one of the following methods:

Choose Run To Cursor from the Code editor Context menu.

Choose Run|Run To Cursor.

Press F4 (default key mapping).

When you run to the cursor, your program executes at full speed until the execution reaches the location marked by the text cursor in the Code editor. When the execution encounters the code marked by the cursor, the debugger regains control and places the execution point on that line of code.

# DebuggerTheExecutionPoint The execution point

The execution point marks the next line of source code to be executed by the debugger. Whenever you pause the program execution within the debugger (for example, whenever you run to the cursor or step to a program location), the debugger highlights a line of code, marking the location of the execution point. The execution point always shows the next line of code to be executed, whether you are going to trace, step, or run your program at full speed.


Stepping through code

Section topics	See also

The Trace Into and Step Over commands offer the simplest way of moving through your program code. While the two commands are very similar, they each offer a different way to step through code. When you step, you watch your program execute statements one at a time. After stepping, the program execution is paused, and you can use the debugger to investigate the different aspects of your program.

# DebuggerTraceInto Trace Into

The Trace Into command executes a single program statement at a time. If the execution point is located on a call to a function that was compiled with debugging information, choosing Trace Into causes the debugger to step into that function by placing the execution point on the function's first statement. However, if the execution point is located on a function call that doesn't contain debugging information (a library function, for example), then choosing Trace Into runs that function at full speed, after which the execution point is positioned on the statement following the function call.

If the execution point is located on the last statement of a function, choosing Trace Into causes the debugger to return from the function, placing the execution point on the line of code that follows the call to the function you are returning from.

The term single stepping refers to using Trace Into to successively run though the statements in your program code.

To issue the Trace Into command, choose one of the following methods:

Choose Run|Trace Into.

Press F7 (default key mapping).

Click the Trace Into SpeedButton.

# DebuggerStepOver Step Over

The Step Over command, like Trace Into, enables you to execute program statements one at a time. However, if you issue the Step Over command when the execution point is located on a function call, the debugger runs that function at full speed (instead of tracing into it), then positions the execution point on the statement that follows the function call.

To issue the Step Over command, choose from one of the following methods:

Choose Run|Step Over.

Press F8 (default key mapping).

Click the Step Over SpeedButton.

As you debug, you can choose to trace into some functions and step over others. If you know a function performs as it was designed to, you can step over calls to that function with confidence that the function call will not cause an error. If, on the other hand, you aren't sure that a function is well behaved, you can choose to Trace Into the function to verify that it works as designed.

# DebuggerDebuggingYourApplicationStartupCode Debugging your application startup code

By default, when you initiate a debugging session by choosing Trace Into or Step Over, Delphi moves the execution point to the first line of code that contains debugging information (this is normally a location that contains user-written code). However, you can override this behavior and have Delphi step to the first executable statement in the program when you begin a debugging session. This feature provides the ability to debug the startup routines of your project.

To have Delphi step to the first executable statement in the program:

1.	Choose Tools|Environment Options, then click the Preferences page tab.

2.	Check the Step Program Block check box.

3.	Run your program by choosing Trace Into or Step Over.


Running to a breakpoint

Section topics	See also

You set breakpoints on lines of source code where you want the program execution to pause during a run. Running to a breakpoint is similar to running to a cursor position in that the program runs at full speed until it reaches a certain source code location. However, unlike Run To Cursor, you can have multiple breakpoints in your code, and you can customize each one so it pauses the program execution only when a specified condition is met. For more information on breakpoints, see "Using breakpoints."


Pausing the program

Section topics	See also

In addition to stepping over or tracing into code, you can also pause your program while it's running. Choosing Run|Program Pause causes the debugger to pause your program the next time the execution point enters your code. You can then use the debugger to examine the state of your program with respect to this program location. When you've finished examining the program, continue debugging by running as usual.


Restarting the program

Section topics	See also

Sometimes while debugging, you will find it necessary to restart the program from the beginning. For example, you might need to restart the program if you step past the location of a bug, or if variables or data structures become corrupted with unwanted values.

To end the current program run and reset the program, choose Run|Program Reset (or press Ctrl+F2).

Resetting a program closes all open program files, releases resources allocated by calls to the VCL, and clears all variable settings. However, resetting a program does not delete any breakpoints or watches that you have set, which makes it easy to resume a debugging session.


Using breakpoints

Section topics	See also

You use breakpoints to pause the program execution at designated source code locations during a debugging session. By setting breakpoints in potential problem areas of your source code, you can run your program at full speed, knowing that its execution will pause at a location you want to debug. When your program execution encounters a breakpoint, the program pauses, and the debugger displays the line containing the breakpoint in the Code editor. You can then use the debugger to view the state of your program.


Setting breakpoints

Section topics	See also

Breakpoints are flexible in that they can be set before you begin a program run or at any time that the integrated debugger has control. For a breakpoint to be valid, it must be set on an executable line of code. Breakpoints set on comment lines, blank lines, declarations, or other non-executable lines of code are invalid and become disabled when you run your program.

To set a breakpoint,
Select the line of code in the Code editor where you want the breakpoint set, then do one of the following:

Click the left margin of the line in the Code editor where you want the breakpoint set.

Press F5 (default key mapping).

Choose Toggle Breakpoint from the Code editor Context menu.

Choose Run|Add Breakpoint, then choose New (in the Edit Breakpoint dialog box) to confirm a new breakpoint setting, or choose Modify to implement changes to an existing breakpoint.

Choose Add Breakpoint from the Breakpoint List Context menu.

Alternately, if you know the line of code where you want the breakpoint set, choose Run|Add Breakpoint and type the source-code line number in the Line Number box. When the settings in the Edit Breakpoint dialog box are correct, choose New to complete the breakpoint entry.

When you set a breakpoint, the line on which the breakpoint is set becomes highlighted, and a stop-sign glyph appears in the left margin of the breakpoint line.

Invalid breakpoints

If a breakpoint is not placed on an executable line of code, the debugger considers it invalid. For example, a breakpoint set on a comment, a blank line, or declaration is invalid. If you set an invalid breakpoint, the debugger displays the Invalid Breakpoint error box when you attempt to run the program. To correct this situation, close the error box and delete the invalid breakpoint from the Breakpoint List window. You can then reset the breakpoint in the intended location. However, you can also ignore invalid breakpoints; Delphi disables any invalid breakpoints when you run your program.

Note:	During the linking phase of compilation, lines of code that do not get called in your program are marked as dead code by the linker. In turn, the integrated debugger marks any breakpoints set on dead code as invalid.

Setting breakpoints after starting a program

While your program is running, you can switch to the debugger (by accessing the Code editor) and set a breakpoint. When you return to your application, the new breakpoint is set, and your application halts when it reaches the breakpoint.


Working with breakpoints

Section topics	See also

The Breakpoint List window lists all breakpoints by their file name and line number. In addition, each breakpoint listing shows any condition and pass count associated with the breakpoint. If a breakpoint is either disabled or invalid, then the breakpoint listing is dimmed in the Breakpoint List window.

To open the Breakpoint List window,
Choose View|Breakpoint.

Use the Breakpoint List window to view and maintain all your breakpoints; you don't need to search through your source files to find the breakpoints you've set. Also, using the commands on the Breakpoint List Context menu, you can view or edit the code at any breakpoint location.

Viewing and editing code at a breakpoint

If a breakpoint isn't displayed in the Code editor window, you can use the Breakpoint List window to quickly find the breakpoint's location in your source code.

To use the Breakpoint List window to locate a breakpoint:

1.	Select the breakpoint in the Breakpoint List window.

2.	Choose View Source or Edit Source from the Context menu.

The Code editor is updated to show the breakpoint's location. If you choose View Breakpoint, the Breakpoint List window remains active so you can modify the breakpoint or go on to view another. If you choose Edit Breakpoint, the Code editor gains focus on the line containing the breakpoint, enabling you to modify the source code at that location.

Disabling and enabling breakpoints

Disabling a breakpoint hides the breakpoint from the current program run. When you disable a breakpoint, all the breakpoint settings remain defined, but the breakpoint is hidden from the execution of your program; your program will not stop on a disabled breakpoint. Disabling a breakpoint is useful if you have defined a conditional breakpoint that you don't need to use now, but might need to use at a later time.

To disable a breakpoint:

1.	Open the Breakpoint List window and highlight the breakpoint you want to disable.

2.	Choose Disable Breakpoint from the Breakpoint List Context menu.

You can also disable all current breakpoints by selecting the Disable All Breakpoints command on the Breakpoint List Context menu.

To reenable a breakpoint:

1.	Highlight the breakpoint in the Breakpoint List window.

2.	Choose Enable Breakpoint from the Context menu.

You can also choose Enable All Breakpoints from the Context menu to enable breakpoints.

Deleting breakpoints

When you no longer need to examine the code at a breakpoint location, you can delete the breakpoint from the debugging session. You can delete breakpoints using either the Code editor or the Breakpoint List window:

To delete breakpoints, use the following methods:

From the Code editor, place the text cursor in the line containing the breakpoint and press F5 (or choose Toggle Breakpoint from the context menu).

Click the stop-sign glyph in the Code editor window to delete the associated breakpoint.

From the Breakpoint List window, highlight the breakpoint you want removed, then choose Delete Breakpoint from the context menu.

To delete all currently set breakpoints, choose Delete All Breakpoints from the Breakpoint List context menu.

Caution:	The breakpoint delete commands are not reversible.


Modifying breakpoint properties

Section topics	See also

To view and modify the properties of a breakpoint, double-click the breakpoint in the Breakpoint List window or select it and choose Edit Breakpoint from the Context menu. The Edit Breakpoint dialog box appears:

In addition to examining a breakpoint's properties, you can use the Edit Breakpoint dialog box to change the location of a breakpoint, set a conditional breakpoint, and set a pass count for the breakpoint.

Creating conditional breakpoints

When a breakpoint is first set, by default it pauses the program execution each time the breakpoint is encountered. However, using the Edit Breakpoint dialog box, you can customize your breakpoints so that they are activated only when a specified set of conditions is met.

Use the integrated debugger to add two types of conditions to your breakpoints:

Boolean expressions

Pass counts

Setting Boolean conditions

The Condition edit box in the Edit Breakpoint dialog box lets you enter an expression that is evaluated each time the breakpoint is encountered during the program execution. If the expression evaluates to True (or not zero), then the breakpoint pauses the program run. If the condition evaluates to False (or zero), then the debugger does not stop at the breakpoint location.

Conditional breakpoints are useful when you want to see how your program behaves when a variable falls into a certain range or what happens when a particular flag is set.

For example, suppose you want a breakpoint to pause on a line of code only when the variable mediumCount is greater than 10. To do so:

1.	Set a breakpoint on the line of code you want by moving the text cursor to that line of code in the Code editor and pressing F5.

2.	Open the Breakpoint List window by choosing View|Breakpoints.

3.	Highlight the breakpoint just set and choose Edit Breakpoint from the Context menu. The Edit Breakpoint dialog box opens.

4.	Enter the following expression into the Condition edit box:

  mediumCount > 10

5.	Choose Modify to confirm your settings.

Note:	You can input any valid language expression into the Condition edit box, but all symbols in the expression must be accessible from the breakpoint's location, and the expression cannot contain any function calls.

You can also use the Assert procedure to test for Boolean conditions while debugging. See the online Help for details.

Using pass counts

The Pass Count edit box enables you to specify a particular number of times that a breakpoint must be passed for the breakpoint to be activated; pass counts tell the debugger to pause the program execution the nth time that the breakpoint is encountered during the program run (the number n is user-specified and is set to 1 by default).

The pass count number is decremented each time the line containing the breakpoint is encountered during the program execution. If the pass count equals 1 when the breakpoint line is encountered, the breakpoint activates and pauses the program execution on that line of code.

Pass counts are useful, for example, when you think that a loop is failing on the nth iteration. Or, if you can't tell on which iteration the loop fails, you can set the pass count to the maximum loop count and run your program. When the program fails, calculate the number of times that the program went through the loop by examining the number in the pass count field.

When pass counts are used in conjunction with Boolean conditions, the breakpoint pauses the program execution the nth time that the condition is true; the condition must be true for the pass count to be decremented.


Customizing the breakpoint and execution point colors

Section topics	See also

You can customize the colors used to indicate the execution point and the enabled, disabled, and invalid breakpoint lines.

To set execution point and breakpoint colors:

1.	Choose Tools|Environment Options.

2.	Select the Colors page tab.

3.	From the Element list, select Execution Point, Active Break, Disabled Break, or Invalid Break, then set the background and foreground colors as you want.


Examining program data values

Section topics	See also

Even though you can discover many interesting things about your program by running and stepping through it, you usually need to examine the values of program variables to uncover bugs. For example, it's helpful to know the value of the index variable as you step though a for loop, or the values of the parameters passed to a function call.

The integrated debugger has the following tools to help you examine the data values in your program:

The Watch List window

The Evaluate/Modify dialog box

Tool Tip Expression Evaluation

The Call Stack window

Data evaluation operates at the level of expressions. An expression consists of constants, variables, and values contained in data structures, combined with language operators. In fact, almost anything you can use as the right side of an assignment operator can be used as a debugging expression, with the exception of function calls.


Watching expressions

Section topics	See also

You use watches to monitor the changing values of variables or expressions during your program run. After you enter a watch expression, the Watch List window displays the current value of the expression. As your program runs, the value of the watch changes as your program updates the values of the variables contained in the watch expression.

If the execution point moves to a location where any of the variables in the watch expression are undefined, then the entire watch expression becomes undefined. If the execution point returns to a location where the watch expression can be evaluated, then the Watch List window again displays the current value of the watch expression.

To open the Watch List window,
Choose View|Watches.

To set a watch from the Code editor:

1.	Select the expression you want to monitor.

2.	Choose Set Watch from the Code editor Context menu to add the expression to the Watch List window.

You can also create a watch by:

1.	Open the Watch Properties dialog box using any of the following techniques:

From the Main menu, choose Run|Add Watch.

Choose Add Watch At Cursor from the Code editor Context menu.

Press Ctrl+F5 (default key mapping).

Double-click a watch in the Watch List window.

Select a watch in the Watch List window, then choose Edit from the Context menu.

2.	In the Expression edit box, enter the expression you want to watch (or choose a previously entered expression from the drop-down list).

Formatting watch expressions

You can format the display of a watch expression using the Watch Properties dialog box. By default, the debugger displays integer values in decimal form. However, by checking the Hex Integer button in the Watch Properties dialog box, you can specify that an integer watch be displayed as hexadecimal:

If you're setting up a watch on an element in a data structure (such as an array), you can display the values of consecutive data elements. For example, suppose you have an array of five integers named xarray. Type the number 5 in the Repeat Count box of the Watch Properties dialog box to see all five values of the array. However, to use a repeat count, the watch expression must represent a single data element.

To format a floating-point expression,
Indicate the number of significant digits you want displayed in the Watch List window by typing this number in the Digits edit box.

Disabling a watch

If you prefer not to watch an expression that you've entered in the Watch List window, but you don't want to delete it because you might need to use it later, you can disable the watch. Disabling watches speeds up the response of the debugger because it won't have to monitor the watch as you step or run through your program.

In the Watch List window, the Enable check box lies to the left of each watch listing. When you set a watch, the debugger enables the watch by checking this box.

To disable a watch, do one of the following:

Double-click a watch in the Watch List window to open the Watch Properties dialog box, then uncheck the Enabled check box.

Select the watch in the Watch window and choose Disable Watch from the Context menu.

You can also disable all current watches by choosing Disable All Watches from the Context menu. To reenable a watch, recheck the Enabled check box, or select it in the Watches window, then choose Enable Watch (or Enable All Watches) from the Context menu.

Deleting a watch

To delete a watch expression:

1.	Select the expression in the Watch window.

2.	Choose Delete Watch from the Context menu.

You can also delete all the watches by choosing Delete All Watches from the Context menu.

Caution:	The delete watch commands cannot be reversed.


Evaluating and modifying expressions

Section topics	See also

You can evaluate expressions and change the values of data items using the Evaluate/Modify dialog box. The Evaluate/Modify dialog box has the advantage over watches in that it enables you to change the values of variables and items in data structures during the course of your debugging session. This can be useful if you think you've found the solution to a bug, and you want to try the correction before exiting the debugger, changing the source code, and recompiling the program.

Evaluating expressions

Choose Run|Evaluate/Modify to open the Evaluate/Modify dialog box:

By default, the word at the cursor position in the current Code editor is placed in the Expression edit box. You can accept this expression, enter another one, or choose an expression from the history list of expressions you've previously evaluated.

To evaluate the expression:

Click the Evaluate button at the bottom of the Evaluate/Modify dialog box.

Using this dialog box, you can evaluate any valid language expression, except those that contain

Local or static variables that are not accessible from the current execution point

Function and procedure calls

Note:	The Evaluate/Modify dialog box lets you view and modify the values of object properties while running the program. However, to view a property, you need to explicitly specify the property name. For example, you can enter the following expression to evaluate the Caption property of Button1: Button1.Caption

When you evaluate an expression, the current value of the expression is displayed in the Result field of the dialog box. If necessary, you can format the result by adding a comma and one or more format following specifiers to the end of the expression entered in the Expression edit box. The following table details the legal format specifiers.

Character	Types affected	Function
H or X	Integers	Hexadecimal. Shows integer values in hexadecimal with the 0x prefix, including those in data structures.
C	Char, strings	Character. Shows special display characters for ASCII 0 to 31. By default, such characters shown using the appropriate C escape sequences (/n, /t, and so on).
D	Integers	Decimal. Shows integer values in decimal form, including those in data structures.
Fn	Floating point	Floating point. Shows n significant digits (where n is in the range 2 to 18, and 7 is the default).
nM	All	Memory dump. Shows n bytes starting at the address of the indicated expression. If n is not specified, it defaults to the size in bytes of the type of the variable.By default, each byte shows as two hex digits. The C, D, H, S, and X specifiers can be used with M to change the byte formatting.
P	Pointers	Pointer. Shows pointers as 8-digit hexadecimal addresses.
R	Records, classes, and objects	Records/Classes/Objects. Shows both field names and values such as (X:1;Y:10;Z:5) instead of (1,10,5).
S	Char, strings	String. Shows any non-displayable ASCII characters as #nn, where nn represents the ordinal value of the ASCII character. Use only to modify memory dumps (see nM).

For example, to display a result in hexadecimal, type ,H after the expression. To see a floating point number to 3 decimal places, type ,F3 after the expression.

Modifying the values of variables

Once you've evaluated a variable or data structure item, you can modify its value. Modifying the value of data items during a debugging session enables you to test different bug hypotheses and see how a section of code behaves under different circumstances.

To modify the value of a data item:

1.	Open the Evaluate/Modify dialog box, then enter the name of the variable or data item you want to modify in the Expression edit box.

2.	Click Evaluate to evaluate the data item.

3.	Type a value into the New Value edit box (or choose a value from the drop down box), then click Modify to update the data item.

Note:	When you modify the value of a data item through the debugger, the modification is effective for that specific program run only; the changes you make through the Evaluate/Modify dialog box do not effect your program source code or the compiled program. To make your change permanent, you must modify your program source code in the Code editor, then recompile your program.

Keep these points in mind when you modify program data values:

You can change individual variables or elements of arrays and data structures, but you cannot change the contents of an entire array or data structure.

The expression in the New Value box needs to evaluate to a result that is assignment-compatible with the variable you want to assign it to. A good rule of thumb is that if the assignment would cause a compile-time or runtime error, it's not a legal modification value.

You can't directly modify untyped parameters passed into a function, but you can typecast them and then assign new values.

Caution:	Modifying values (especially pointer values and array indexes) can have undesirable effects because you can overwrite other variables and data structures. Use caution whenever you modify program values from the debugger.

Tool Tip Expression Evaluation

Tool Tip evaluation gives you a fast way to check the value of an expression when a program is stopped during a debugging session. You can point to a variable in the Code editor, and a pop-up window displays the value of that variable. For more information about Tool Tip evaluation, see "What kind of help do you want while coding?"


Viewing function calls

Section topics	See also

While debugging, it can be useful to know the order of function calls that brought you to your current program location. Using the Call Stack window, you can view the current sequence of function calls. The Call Stack window is also helpful when you want to view the arguments passed to a function call; each function listing in the window is followed by a listing that details the parameters with which the call was made.

To display the Call Stack window,
Choose View|Call Stack.

The top of the Call Stack window lists the last function called by your program. Below this is the listing for the previously called function. The listing continues, with the first function called in your program located at the bottom of the list. Note that only functions in the currently loaded symbol table are listed in this window. To view functions located in a different module, change symbol tables, as described in later topics.

The Call Stack window is particularly useful if you accidentally trace into code you wanted to step over. Using the Call Stack window, you can return to the point from which the current function was called, then resume debugging from there.

To use the Call Stack window:

1.	In the Call Stack window, double-click the function that called the function you accidently traced into (it is the second function listed in the Call Stack window). The Code editor becomes active with the cursor positioned at the location of the function call.

2.	In the Code editor, move the cursor to the statement following the function call.

3.	Choose Run To Cursor on the Code editor Context menu (or press F4).


Navigating to function calls

Section topics	See also

Using the Call Stack window, you can view or edit the source code located at a particular function call.

To view or edit the source code of a function call,
Choose either View Source or Edit Source (as appropriate) from the Call Stack context menu.

The View Source and Edit Source commands each cause the Code editor to navigate to the selected function. However, Edit Source gives focus to the Code editor so you can modify the source code at that function location, while View Source does not move the focus from the Call Stack window.

If you select the top function in the Call Stack window, View Source and Edit Source cause the Code editor to display the location of the execution point in the current function. Selecting any other function call causes the debugger to display the actual function call in the Code editor.


Handling hardware and language exceptions

Section topics	See also

Delphi provides a mechanism to control the way exceptions are handled while you are debugging. In addition, Delphi makes the debugging session easier by treating most hardware exceptions as Object Pascal language exceptions. This means Delphi traps the hardware exceptions generated by your Delphi application, and you can gracefully recover before your program run ends with a system crash.

If a hardware or language exception occurs during a debugging session, the VCL displays a dialog box that notes the exception (note that the VCL displays this dialog box regardless of whether you are debugging the program). When you choose OK to close the dialog box, your program run continues and you can continue to debug your application.

To have Delphi pause the program run when an exception occurs:

1.	Choose Tools|Environment Options, then click the Preferences page tab.

2.	Check the Break On Exception check box (checked by default).

3.	Begin your debugging session.

If you check Break On Exception, the Delphi displays an Error dialog box whenever an exception occurs. When you choose OK to close this dialog box, Delphi opens the Code editor with the execution point positioned on the code that caused the exception. At this point, you can examine the program code to determine why the exception occurred. If you continue the program run from this point, the VCL displays an exception dialog box.

Note:	Because certain hardware exceptions can place your system in an unstable condition, it is usually best to terminate your program run after an exception occurs. Resolve the program error, then resume the debugging session.


See also

Making effective use of the development environment


Handling exceptions

Section topics	See also

Delphi provides you with a mechanism to ensure that your applications are robust, meaning that they handle errors in a consistent manner that allows the application to recover from errors if possible and to shut down if need be, without losing data or resources.

Error conditions in Delphi are indicated by exceptions.

To use exceptions to create safe applications, you need to understand the following tasks:

Protecting blocks of code

Protecting resource allocations

Handling RTL exceptions

Handling component exceptions

Silent exceptions

Defining your own exceptions


Protecting blocks of code

Section topics	See also

To make your applications robust, your code needs to recognize exceptions when they occur and respond to them. If you don't specify a response, the application will present a message box describing the error. Your job, then, is to recognize places where errors might happen, and define responses, particularly in areas where errors could cause the loss of data or system resources.

When you create a response to an exception, you do so on blocks of code. When you have a series of statements that all require the same kind of response to errors, you can group them into a block and define error responses that apply to the whole block.

Blocks with specific responses to exceptions are called protected blocks because they can guard against errors that might otherwise either terminate the application or damage data.

To protect blocks of code you need to understand

Responding to exceptions

Exceptions and the flow of execution

Nesting exception responses


Responding to exceptions

Section topics	See also

When an error condition occurs, the application raises an exception, meaning it creates an exception object. Once an exception is raised, your application can execute cleanup code, handle the exception, or both.

Executing cleanup code

The simplest way to respond to an exception is to guarantee that some cleanup code is executed. This kind of response doesn't correct the condition that caused the error but lets you ensure that your application doesn't leave its environment in an unstable state.

You typically use this kind of response to ensure that the application frees allocated resources, regardless of whether errors occur.

Handling the exception

Handling an exception means making a specific response to a specific kind of exception. This clears the error condition and destroys the exception object, which allows the application to continue execution.

You typically define exception handlers to allow your applications to recover from errors and continue running. Types of exceptions you might handle include attempts to open files that don't exist, writing to full disks, or calculations that exceed legal bounds. Some of these, such as "File not found," are easy to correct and retry, while others, such as running out of memory, might be more difficult for the application or the user to correct.


Exceptions and the flow of execution

Section topics	See also

Object Pascal makes it easy to incorporate error handling into your applications because exceptions don't get in the way of the normal flow of your code. In fact, by moving error checking and error handling out of the main flow of your algorithms, exceptions can simplify the code you write.

When you declare a protected block, you define specific responses to exceptions that might occur within that block. When an exception occurs in that block, execution immediately jumps to the response you defined, then leaves the block.

Example

Here's some code that includes a protected block. If any exception occurs in the protected block, execution jumps to the exception-handling part, which beeps. Execution resumes outside the block.

...
try	{ begin the protected block }
  Font.Name := 'Courier';	{ if any exception occurs... }

 Font.Size := 24;	{ ...in any of these statements... }

 Color := clBlue;

except	{ ...execution jumps to here }
  on Exception do MessageBeep(0);	{ this handles any exception by beeping }

end;
...	{ execution resumes here, outside the protected block}


Nesting exception responses

Section topics	See also

Your code defines responses to exceptions that occur within blocks. Because Pascal allows you to nest blocks inside other blocks, you can customize responses even within blocks that already customize responses.

In the simplest case, for example, you can protect a resource allocation, and within that protected block, define blocks that allocate and protect other resources. Conceptually, that might look something like this:

You can also use nested blocks to define local handling for specific exceptions that overrides the handling in the surrounding block. Conceptually, that looks something like this:

You can also mix different kinds of exception-response blocks, nesting resource protections within exception handling blocks and vice versa.


Protecting resource allocations (try..finally block)
Protecting resource allocations

Section topics	See also

One key to having a robust application is ensuring that if it allocates resources, it also releases them, even if an exception occurs. For example, if your application allocates memory, you need to make sure it eventually releases the memory, too. If it opens a file, you need to make sure it closes the file later.

Keep in mind that exceptions don't come just from your code. A call to an RTL routine, for example, or another component in your application might raise an exception. Your code needs to ensure that if these conditions occur, you release allocated resources.

To protect resources effectively, you need to understand the following:

What kind of resources need protection?

Creating a resource-protection block


What kind of resources need protection?

Section topics	See also

Under normal circumstances, you can ensure that an application frees allocated resources by including code for both allocating and freeing. When exceptions occur, however, you need to ensure that the application still executes the resource-freeing code.

Some common resources that you should always be sure to release are:

Files

Memory

Windows resources

Objects

Example

The following event handler allocates memory, then generates an error, so it never executes the code to free the memory:

procedure TForm1.Button1Click(Sender: TComponent);

var
  APointer: Pointer;
  AnInteger, ADividend: Integer;

begin
  ADividend := 0;

 GetMem(APointer, 1024);	{ allocate 1K of memory }

 AnInteger := 10 div ADividend;	{ this generates an error }

 FreeMem(APointer, 1024);	{ it never gets here }
end;

Although most errors are not that obvious, the example illustrates an important point: When the division-by-zero error occurs, execution jumps out of the block, so the FreeMem statement never gets to free the memory.

In order to guarantee that the FreeMem gets to free the memory allocated by GetMem, you need to put the code in a resource-protection block.


Creating a resource-protection block

Section topics	See also

To ensure that you free allocated resources, even in case of an exception, you embed the resource-using code in a protected block, with the resource-freeing code in a special part of the block. Here's an outline of a typical protected resource allocation:

{ allocate the resource }

try
  { statements that use the resource }

finally
  { free the resource }

end;

The key to the try..finally block is that the application always executes any statements in the finally part of the block, even if an exception occurs in the protected block. When any code in the try part of the block (or any routine called by code in the try part) raises an exception, execution immediately jumps to the finally part, which is called the cleanup code. If no exception occurs, the cleanup code is executed in the normal order, after all the statements in the try part.

Example

Here's an event handler that allocates memory and generates an error, but still frees the allocated memory:

procedure TForm1.Button1Click(Sender: TComponent);

var
  APointer: Pointer;
  AnInteger, ADividend: Integer;

begin
  ADividend := 0;

 GetMem(APointer, 1024);	{ allocate 1K of memory }

 try
    AnInteger := 10 div ADividend;	{ this generates an error }

 finally
    FreeMem(APointer, 1024);	{ execution resumes here, despite the error }

 end;

end;

The statements in the termination code do not depend on an exception occurring. If no statement in the try part raises an exception, execution continues through the termination code.

Note:	A resource-protection block doesn't handle the exception. In fact, the termination code doesn't have information about whether an exception even occurred, so it can't determine whether it needs to handle an exception. If an exception occurs in a resource-protection block, execution first goes to the termination code, then leaves the block with the exception still raised. The block that contains the protected block can then respond to the exception.


Handling RTL exceptions

Section topics	See also

When you write code that calls routines in the run-time library (RTL), such as mathematical functions or file-handling procedures, the RTL reports errors back to your application in the form of exceptions. By default, RTL exceptions generate a message that the application displays to the user. You can define your own exception handlers to handle RTL exceptions in other ways.

There are also silent exceptions that do not, by default, display a message.

To handle RTL exceptions effectively, you need to understand the following:

What are the RTL exceptions?

Creating an exception handler

Providing default exception handlers

Handling classes of exceptions

Reraising the exception


What are the RTL exceptions?

Section topics	See also

The run-time library's exceptions are defined in the SysUtils unit, and they all descend from a generic exception-object type called Exception. Exception provides the string for the message that RTL exceptions display by default.

There are seven kinds of exceptions raised by the RTL, as described in the following table.

Error type	Cause	Meaning
Input/output	Error accessing a file or I/O device	Most I/O exceptions are related to error codes returned by Windows when accessing a file.
Heap	Error using dynamic memory	Heap errors can occur when there is insufficient memory available, or when an application disposes of a pointer that points outside the heap.
Integer math	Illegal operation on integer-type expressions	Errors include division by zero, numbers or expressions out of range, and overflows.
Floating-point math	Illegal operation on real-type expressions	Floating-point errors can come from either a hardware coprocessor or the software emulator. Errors include invalid instructions, division by zero, and overflow or underflow.
Typecast	Invalid typecasting with the as operator	Objects can only be typecast to compatible types.
Conversion	Invalid type conversion	Type-conversion functions such as IntToStr, StrToInt, and StrToFloat raise conversion exceptions when the parameter cannot be converted to the desired type.
Hardware	System condition	Hardware exceptions indicate that either the processor or the user generated some kind of error condition or interruption, such as an access violation, stack overflow, or keyboard interrupt.
Variant	Illegal type coercion	Errors can occur when referring to variants in expressions where the variant cannot be coerced into a compatible type.

For a list of the RTL exception types, see the SysUtils unit.


Creating an exception handler (try..except block)
Creating an exception handler

Section topics	See also

An exception handler is code that handles a specific exception or exceptions that occur within a protected block of code.

To define an exception handler,

Embed the code you want to protect in an exception-handling block and specify the exception handling statements in the except part of the block.

Here is an outline of a typical exception-handling block:

try
  { statements you want to protect }

except
  { exception-handling statements }

end;

The application executes the statements in the except part only if an exception occurs during execution of the statements in the try part. Execution of the try part statements includes routines called by code in the try part. That is, if code in the try part calls a routine that doesn't define its own exception handler, execution returns to the exception-handling block, which handles the exception.

When a statement in the try part raises an exception, execution immediately jumps to the except part, where it steps through the specified exception-handling statements, or exception handlers, until it finds a handler that applies to the current exception.

Once the application locates an exception handler that handles the exception, it executes the statement, then automatically destroys the exception object. Execution continues at the end of the current block.


Exception-handling statements

Section topics	See also

Each statement in the except part of a try..except block defines code to execute to handle a particular kind of exception. The form of these exception-handling statements is as follows:

on <type of exception> do <statement>;

Example

You can define an exception handler for division by zero to provide a default result:

function GetAverage(Sum, NumberOfItems: Integer): Integer;
begin

 try
    Result := Sum div NumberOfItems;	{ handle the normal case }

 except
    on EDivByZero do Result := 0;	{ handle the exception only if needed }

 end;

end;

Note that this is clearer than having to test for zero every time you call the function. Here's an equivalent function that doesn't take advantage of exceptions:

function GetAverage(Sum, NumberOfItems: Integer): Integer;
begin

 if NumberOfItems <> 0 then	{ always test }
    Result := Sum div NumberOfItems	{ use normal calculation }
  else Result := 0;	{ handle exceptional case }

end;

The difference between these two functions really defines the difference between programming with exceptions and programming without them. This example is quite simple, but you can imagine a more complex calculation involving hundreds of steps, any one of which could fail if one of dozens of inputs were invalid.

By using exceptions, you can spell out the "normal" expression of your algorithm, then provide for those exceptional cases when it doesn't apply. Without exceptions, you have to test every single time to make sure you're allowed to proceed with each step in the calculation.


Using the exception instance

Section topics	See also

Most of the time, an exception handler doesn't need any information about an exception other than its type, so the statements following on..do are specific only to the type of exception. In some cases, however, you might need some of the information contained in the exception instance.

To read specific information about an exception instance in an exception handler, you use a special variation of on..do that gives you access to the exception instance. The special form requires that you provide a temporary variable to hold the instance.

Example

If you create a new project that contains a single form, you can add a scroll bar and a command button to the form. Double-click the button and add the following line to its click-event handler:

ScrollBar1.Max := ScrollBar1.Min - 1;

That line raises an exception because the maximum value of a scroll bar must always exceed the minimum value. The default exception handler for the application opens a dialog box containing the message in the exception object. You can override the exception handling in this handler and create your own message box containing the exception's message string:

try
  ScrollBar1.Max := ScrollBar1.Min - 1;

except
  on E: EInvalidOperation do
    MessageDlg('Ignoring exception: ' + E.Message, mtInformation, [mbOK], 0);

end;

The temporary variable (E in this example) is of the type specified after the colon (EInvalidOperation in this example). You can use the as operator to typecast the exception into a more specific type if needed.

Note:	Never destroy the temporary exception object. Handling an exception automatically destroys the exception object. If you destroy the object yourself, the application attempts to destroy the object again, generating an access violation.


Scope of exception handlers

Section topics	See also

You don't have to provide handlers for every kind of exception in every block. In fact, you need to provide handlers only for those exceptions you want to handle specially within that particular block.

If a block doesn't handle a particular exception, execution leaves that block and returns to the block that contains the block (or to the code that called the block), with the exception still raised. This process repeats with increasingly broad scope until either execution reaches the outermost scope of the application or a block at some level handles the exception.

Providing default exception handlers

You can provide a single default exception handler to handle any exceptions you haven't provided specific handlers for. To do that, you add an else part to the except part of the exception-handling block:

try
  { statements }

except
  on ESomething do { specific exception-handling code };

 else { default exception-handling code };
end;

Adding default exception handling to a block guarantees that the block handles every exception in some way, thereby overriding all handling from the containing block.

Caution:	You should probably never use this all-encompassing default exception handler. The else clause handles all exceptions, including those you know nothing about. In general, your code should handle only exceptions you actually know how to handle. In other cases, it's better to execute cleanup code and leave the handling to code that has more information about the exception and how to handle it.


Handling classes of exceptions

Section topics	See also

Because exception objects are part of a hierarchy, you can specify handlers for entire parts of the hierarchy by providing a handler for the exception class from which that part of the hierarchy descends.

Example

The following block outlines an example that handles all integer math exceptions specially:

try
  { statements that perform integer math operations }

except
  on EIntError do { special handling for integer math errors };
end;

You can still specify specific handlers for more specific exceptions, but you need to place those handlers above the generic handler, because the application searches the handlers in the order they appear in, and executes the first applicable handler it finds.

For example, this block provides special handling for range errors, and other handling for all other integer math errors:

try
  { statements performing integer math }

except
  on ERangeError do { out-of-range handling };

 on EIntError do { handling for other integer math errors };
end;

Note that if the handler for EIntError came before the handler for ERangeError, execution would never reach the specific handler for ERangeError.


Reraising the exception

Section topics	See also

Sometimes when you handle an exception locally, you actually want to augment the handling in the enclosing block, rather than replacing it. Of course, when your local handler finishes its handling, it destroys the exception instance, so the enclosing block's handler never gets to act. You can, however, prevent the handler from destroying the exception, giving the enclosing handler a chance to respond.

Example

When an exception occurs, you might want to display some sort of message to the user, then proceed with the standard handling. To do that, you declare a local exception handler that displays the message then calls the reserved word raise. This is called reraising the exception, as shown in this example:

try
  { statements }

 try
    { special statements }

 except
    on ESomething do
    begin
      { handling for only the special statements }

     raise;	{ reraise the exception }
    end;
  end;

except
  on ESomething do ...;	{ handling you want in all cases }
end;

If code in the { statements } part raises an ESomething exception, only the handler in the outer except part executes. However, if code in the { special statements } part raises ESomething, the handling in the inner except part is executed, followed by the more general handling in the outer except part.

By reraising exceptions, you can easily provide special handling for exceptions in special cases without losing (or duplicating) the existing handlers.


Handling component exceptions

Section topics	See also

Delphi's components raise exceptions to indicate error conditions. Most component exceptions indicate programming errors that would otherwise generate a run-time error. The mechanics of handling component exceptions are no different than handling RTL exceptions.

Example

A common source of errors in components is range errors in indexed properties. For example, if a list box has three items in its list (0..2) and your application attempts to access item number 3, the list box raises an "Index out of range" exception.

The following event handler contains an exception handler to notify the user of invalid index access in a list box:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ListBox1.Items.Add('a string');	{ add a string to list box }

 ListBox1.Items.Add('another string');	{ add another string... }

 ListBox1.Items.Add('still another string');	{ ...and a third string }

 try
    Caption := ListBox1.Items[3];	{ set form caption to fourth string in list box }

 except
    on EStringListError do
      MessageDlg('List box contains fewer than four strings', mtWarning, [mbOK], 0);

 end;
end;

If you click the button once, the list box has only three strings, so accessing the fourth string (Items[3]) raises an exception. Clicking a second time adds more strings to the list, so it no longer causes the exception.


Silent exceptions

Section topics	See also

Delphi applications handle most exceptions that your code doesn't specifically handle by displaying a message box that shows the message string from the exception object. You can also define "silent" exceptions that do not, by default, cause the application to show the error message.

Silent exceptions are useful when you don't intend to handle an exception, but you want to abort an operation. Aborting an operation is similar to using the Break or Exit procedures to break out of a block, but can break out of several nested levels of blocks.

Silent exceptions all descend from the standard exception type EAbort. The default exception handler for Delphi applications displays the error-message dialog box for all exceptions that reach it except those descended from EAbort.

There is a shortcut for raising silent exceptions. Instead of manually constructing the object, you can call the Abort procedure. Abort automatically raises an EAbort exception, which will break out of the current operation without displaying an error message.

Example

The following code shows a simple example of aborting an operation. On a form containing an empty list box and a button, attach the following code to the button's OnClick event:

procedure TForm1.Button1Click(Sender: TObject);

var
  I: Integer;

begin

 for I := 1 to 10 do	{ loop ten times }
  begin

   ListBox1.Items.Add(IntToStr(I));	{ add a numeral to the list }

   if I = 7 then Abort;	{ abort after the seventh one }

 end;
end;


Defining your own exceptions

Section topics	See also

In addition to protecting your code from exceptions generated by the run-time library and various components, you can use the same mechanism to manage exceptional conditions in your own code.

To use exceptions in your code, you need to understand these steps:

Declaring an exception object type

Raising an exception


Declaring an exception object type

Section topics	See also

Because exceptions are objects, defining a new kind of exception is as simple as declaring a new object type. Although you can raise any object instance as an exception, the standard exception handlers handle only exceptions descended from Exception.

It's therefore a good idea to derive any new exception types from Exception or one of the other standard exceptions. That way, if you raise your new exception in a block of code that isn't protected by a specific exception handler for that exception, one of the standard handlers will handle it instead.

Example

For example, consider the following declaration:

type
  EMyException = class(Exception);

If you raise EMyException but don't provide a specific handler for EMyException, a handler for Exception (or a default exception handler) will still handle it. Because the standard handling for Exception displays the name of the exception raised, you could at least see that it was your new exception raised.


Raising an exception

Section topics	See also

To indicate an error condition in an application, you can raise an exception which involves constructing an instance of that type and calling the reserved word raise.

To raise an exception,
Call the reserved word raise, followed by an instance of an exception object.

When an exception handler actually handles the exception, it finishes by destroying the exception instance, so you never need to do that yourself.

Setting the exception address

Raising an exception sets the ErrorAddr variable in the System unit to the address where the application raised the exception. You can refer to ErrorAddr in your exception handlers, for example, to notify the user of where the error occurred. You can also specify a value for ErrorAddr when you raise an exception.

To specify an error address for an exception,
Add the reserved word at after the exception instance, followed by an address expression such as an identifier.

Examples

For example, given the following declaration,

type
  EPasswordInvalid = class(Exception);

you can raise a "password invalid" exception at any time by calling raise with an instance of EPasswordInvalid, like this:

if Password <> CorrectPassword then
  raise EPasswordInvalid.Create('Incorrect password entered');


See also

Object Pascal blocks

Understanding Object Pascal units

Object-oriented programming for component writers

Viewing files in the Code editor


Working with string lists

Section topics	See also

There are numerous occasions when Delphi applications need to deal with lists of character strings. Among these lists are the items in a list box or combo box, the lines of text in a memo field, the list of fonts supported by the screen, the names of the tabs on a notepad, the items in an outline, and a row or column of entries in a string grid.

Although applications use these lists in various ways, Delphi provides a common interface to all of them through an object called a string list, and goes even farther by making them interchangeable--meaning you can, for example, edit a string list in a memo field and then use it as the list of items in a list box.

You have probably already used string lists through the Object Inspector. A string-list property appears in the Object Inspector with (TStrings) in the Value column. When you double-click (TStrings), the String List editor appears, where you can edit, add, or delete lines.

You can also work with string lists at runtime. These are the most common types of string-list tasks:

Manipulating the strings in a list

Loading and saving string lists

Creating a new string list

Adding objects to a string list


Manipulating the strings in a list

Section topics	See also

Quite often in an Delphi application, you need to write code to work with strings in an existing string list. The most common case is that some component in the application has a string-list property, and you need to change it or get strings from it.

These are the common operations you might need to perform:

Counting the strings in a list

Accessing a particular string

Finding the position of a string

Adding a string to a list

Moving a string within a list

Deleting a string from a list

Copying a complete string list

Iterating the strings in a list


Counting the strings in a list

Section topics	See also

To find out how many strings are in a string list,
Use the Count property.

Count is a read-only property that indicates the number of strings in the list. Since the indexes used in string lists are zero-based, Count is one more than the index of the last string in the list.

Example

For example, an application can find out how many different fonts the current screen supports by reading the Count property on the screen object's font list, which is a string list containing the names of all the supported fonts:

FontCount := Screen.Fonts.Count;


Accessing a particular string

Section topics	See also

The string list has an indexed property called Strings, which you can treat like an array of strings for most purposes. For example, the first string in the list is Strings[0]. However, since the Strings property is the most common part of a string list to access, Strings is the default property of the list, meaning that you can omit the Strings identifier and just treat the string list itself as an indexed array of strings.

To access a particular string in a string list,
Refer to it by its ordinal position, or index, in the list. The string numbers are zero-based, so if a list has three strings in it, the indexes cover the range 0..2.

To determine the maximum index,
Check the Count property.

If you try to access a string outside the range of valid indexes, the string list raises an exception.

Example

For example, the following two lines do exactly the same thing, setting the first line of text in a memo field:

Memo1.Lines.Strings[0] := 'This is the first line.';
Memo1.Lines[0] := 'This is the first line.';


Finding the position of a string

Section topics	See also

Given a string, you often want to find out its position in a string list (or whether it's in the list at all).

To locate a string in a string list,

Use the string list's IndexOf method.

IndexOf takes a string as its parameter, and returns the index of the matching string in the list, or -1 if the string is not in the list.

Note that IndexOf works only with complete strings. That is, it must find an exact match for the whole string passed to it, and it must match a complete string in the list. If you want to match partial strings (for instance, to see if any of the strings in the list contains a given series of characters), you must iterate the list yourself and compare the strings.

Example

Here's an example of using IndexOf to determine whether a given file name is in the list of files in a file list box:

if FileListBox1.Items.IndexOf('WIN.INI') > -1 then { you're in the Windows directory };


Adding a string to a list

Section topics	See also

There are two ways to add a string to a string list. You can either add it to the end of the list or insert it in the middle of the list.

To add a string to the end of the list,
 Call the Add method, passing the new string as the parameter. The added string becomes the last string in the list.

To insert a string into the list,
 Call the Insert method, passing two parameters: the index where you want the inserted string to appear and the string.

Example

To make the string 'Three' become the third string in a list, you would call Insert(2, 'Three'). If the list doesn't already have at least two strings, Delphi raises an index-out-of-range exception.


Moving a string within a list

Section topics	See also

You can move a string to a different position in a string list, such as when you want to sort the list. If the string has an associated object, the object moves with the string.

To move a string in the list,
Call the Move method, passing two parameters: the current index of the item and the index where you want to move the item to.

For example, to move the third string in a list to the fifth position, you would call Move(2, 4).


Deleting a string from a list

Section topics	See also

To delete a string from a string list,
Call the string list's Delete method, passing the index of the string you want to delete.

If you don't know the index of the string you want to delete, use the IndexOf method to locate it.

To delete all the strings in a string list,
Use the Clear method.

Example

This example uses IndexOf to determine the location of a string in a list, and deletes that string if present:

with ListBox1.Items do
begin

 if IndexOf('bureaucracy') > -1 then
    Delete(IndexOf('bureaucracy'));

end;


Copying a complete string list

Section topics	See also

To copy a list of strings from one string list to another,

Assign the source list to the destination list.

Even if the lists are associated with different kinds of components (or no components at all), Delphi handles the copying of the list for you.

Copying the strings from one list to another overwrites the strings that were originally in the destination list. If you want to add a list of strings to the end of another list, you call the AddStrings method, passing as a parameter the list of strings you want to add.

Examples

This example copies the items from a combo box into an outline:

Outline1.Lines := ComboBox1.Items;

This example adds all the items from the combo box to the end of the outline:

Outline1.AddStrings(ComboBox1.Items);


Iterating the strings in a list

Section topics	See also

Many times you need to perform an operation on each string in a list, such as searching for a particular substring or changing the case of each string.

To iterate through each string in a list,
Use a for loop with an Integer-type index. The loop should run from zero up to one less than the number of strings in the list (Count - 1).

Inside the loop you can access each string and perform the operation you want.

Example

For example, here's a loop that iterates the strings in a list box and converts each one to all uppercase characters in response to a button click:

procedure TForm1.Button1Click(Sender: TObject);
var
  Index: Integer;

begin

 for Index := 0 to ListBox1.Items.Count - 1 do
    ListBox1.Items[Index] := UpperCase(ListBox1.Items[Index]);

end;


Loading and saving string lists

Section topics	See also

You can easily store any Delphi string list in a text file and load it back again (or load it into a different list). String list objects have methods to handle both operations. Using these methods, you can quickly create a simple text editor by loading a file into a memo component (as shown in "Putting documents in child forms"). But you can also use the same mechanism to save lists of items for list boxes or complete outlines.

To load a string list from a file,
 Call the LoadFromFile method and pass the name of the text file to load from. LoadFromFile reads each line from the text file into a string in the list.

To store a string list in a text file,
Call the SaveToFile method and pass it the name of the text file to save to. If the file doesn't already exist, SaveToFile creates it. Otherwise, it overwrites the current contents of the file with the strings from the string list.

Example

This example loads a copy of the WIN.INI file from the Windows directory of the C drive into a memo field and makes a backup copy called WIN.BAK:

procedure TForm1.FormCreate(Sender: TObject);

var
  FileName: string;	{ storage for file name }

begin
  FileName := 'C:\WINDOWS\WIN.INI';	{ set the file name }

 with Memo1.Lines do
  begin

   LoadFromFile(FileName);	{ load from file }

   SaveToFile(ChangeFileExt(FileName, '.BAK'));	{ save into backup file }

 end;

end;


Creating a new string list

Section topics	See also

Most of the time when you use a string list, you use one that is part of a component, so you don't have to construct the list yourself. However, you can also create standalone string lists that have no associated component. For instance, your application might need to keep a list of strings for a lookup table.

The most important thing to remember when you create your own string list is to free the list when you finish with it. There are two distinct cases you might need to handle: a list that the application creates, uses, and destroys all in a single routine, and a list that the application creates, uses throughout runtime, and destroys before it shuts down.

The way you create and manage a string list depends on whether it's going to be a long-term list or a short-term list.


Short-term string lists

Section topics	See also

If you need to use a string list only for the duration of a single routine, you can create it, use it, and destroy it all in one place. This is the safest way to use string list objects.

Because the string list object allocates memory for itself and its strings, it is important that you protect the allocation by using a try..finally block to ensure that the object frees its memory even if an exception occurs.

The basic outline of the use of a short-term string list, then, is to:

1.	Construct the string-list object.

2.	In the try part of a try..finally block, use the string list.

3.	In the finally part, free the string-list object.

Example

The following event handler responds to a button click by constructing a string list, using it, and then destroying it again.

procedure TForm1.Button1Click(Sender: TObject);

var
  TempList: TStrings;	{ declare the list }

begin

 TempList := TStringList.Create;	{ construct the list object }

 try
    { use the string list }

 finally
    TempList.Free;	{ destroy the list object }

 end;

end;


Long-term string lists

Section topics	See also

If you need a string list that's available at any time while your application runs, you need to construct the list when the application is first executed, then destroy it before the application is terminated.

To create a string list that's available throughout runtime:

1.	Add a field of type TStrings to the application's main form object, giving it the name you want to use.

2.	Create a handler for the main form's OnCreate event. The create-event handler executes before the form appears onscreen at runtime.

3.	In the create-event handler, construct the string-list object.

4.	Create a handler for the main form's OnDestroy event. The destroy-event handler executes just after the main form disappears from the screen before the application stops running.

Any other event handlers can then access the string list by the name you declared in the first step.

Example

This example features an added string list named ClickList. The click-event handler for the main form adds a string to the list every time the user clicks a mouse button, and the application writes the list out to a file before destroying the list.

unit Unit1;

interface

uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs;

type
  TForm1 = class(TForm)

   procedure FormCreate(Sender: TObject);

   procedure FormDestroy(Sender: TObject);

   procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);

 private

   { Private declarations }

 public

   { Public declarations }

   ClickList: TStrings;	{ declare the field }

 end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  ClickList := TStringList.Create;	{ construct the list }
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  ClickList.SaveToFile(ChangeFileExt(Application.ExeName, '.LOG'));	{ save the list }

 ClickList.Free;	{ destroy the list object }
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  ClickList.Add(Format('Click at (%d, %d)', [X, Y]));	{ add a string to the list }
end;

end.


Adding objects to a string list

Section topics	See also

In addition to its list of strings, stored in the Strings property, a string list can also have a list of objects, which it stores in its Objects property. Like Strings, Objects is an indexed property, but instead of an indexed list of strings, it is an indexed list of objects.

If you're just using the strings in the list, it doesn't matter whether you have objects; the list does nothing with the objects unless you specifically access them. Also, it doesn't matter what kind of object you assign to the Objects property. Delphi just holds the information; you manipulate it as you need to.

Note:	Some string lists ignore added objects, because it doesn't make sense to have them. For example, the string lists representing the lines in a memo do not store objects added to them. Other string lists, such as the pages in a notebook, already use their associated objects as part of their standard operation. The Notebook component uses its Pages string list to store both the names of the pages and the objects that represent those pages.

Although you can assign any type of objects you want to Objects, the most common use is to associate bitmaps with strings for owner-draw controls. The important thing to remember is that strings and objects in a string list work in pairs. For every string, there is an associated object, although by default, that object is nil.

It's important to understand that the string list doesn't own the objects associated with it. That is, destroying the string-list object does not destroy the objects associated with the strings.


Operating on objects in a string list

Section topics	See also

For each of the operations you can perform on a string list with a string, you can perform the corresponding operation with a string and its associated object. For example, you can access a particular object by indexing into the Objects property, just as you would the Strings property. The biggest difference is that you cannot omit the name Objects, since Strings is the default property of the string list.

The following table summarizes the properties and methods you use to perform corresponding operations on strings and objects in a string list.

Operation	For strings	For objects
Access an item	Strings property	Objects property
Add an item	Add method	AddObject method
Insert an item	Insert method	InsertObject method
Locate an item	IndexOf method	IndexOfObject method

Methods such as Delete, Clear, and Move operate on items as a whole. That is, deleting an item deletes both the string and the corresponding object. Also note that the LoadFromFile and SaveToFile methods operate on only the strings, since they work with text files.

Accessing associated objects

You access objects associated with a string list just as you access the strings. For example, to get the first string in a string list, you access the string list's Strings property at index 0: Strings[0]. The object corresponding to that string is Objects[0].

Adding associated objects

To associate an object with an existing string, you assign the object to the Objects property at the same index. For example, if a string list named Fruits contains the string 'apple' and you want to associate a bitmap called AppleBitmap to it, you would use the following assignment:

with Fruits do Objects[IndexOf('apple')] := AppleBitmap;

You can also add objects at the same time you add the strings by calling the string list's AddObject method instead of Add, which just adds a string. AddObject takes two parameters, the string and the object. For example,

Fruits.AddObject('apple', AppleBitmap);

Note that you can't add an object without a corresponding string.


See also

What is an object?

Creating an owner-draw control


Using Windows common dialogs

Section topics	See also

Windows applications use a set of dialog boxes built into the operating environment to open and save files (including special dialog boxes to open and save picture files), choose fonts and colors, print information, and find and replace information. Collectively, these dialog boxes are known as the Windows common dialog boxes. The components on the Dialogs page of the Component palette make the common dialog boxes available to your Delphi applications.

These topics shows examples of how to work with some of the common dialog boxes.


Adding a common dialog box to a project

Section topics	See also

To add a common dialog box to a project:

1.	Place the common-dialog component on a form.

You'll see only the component's icon at design time. You see the dialog box, itself, when it's called at runtime.

2.	Set properties for the dialog box, such as a title, file-name filters, and so on.

3.	At runtime, call the dialog-box component's Execute method.


Executing common dialog boxes

Section topics	See also

The important thing to remember about common dialog boxes is that they don't perform actions such as file-opening or color-changing themselves. That's still up to your application. The common dialog boxes provide a common interface for common operations such as choosing a file name or selecting a color. Your application then uses that information to perform the appropriate action.

There are two kinds of common dialog boxes: option and summary. An option dialog box presents the user with a number of choices, and returns each of those choices in an individual property. The print dialog box is an example of an option dialog box. A summary dialog box combines all the options chosen by the user into a single, summary property. The font and color dialog boxes are examples of summary dialog boxes.

To use a common dialog box at runtime:

1.	Set the initial value of the summary property (a file name, color, font, and so on) or the initial options.

2.	Call the dialog box component's Execute method.

Execute opens the dialog box, then returns a Boolean value when the user closes the dialog box. If the user chooses OK to accept changes made in the dialog box, Execute returns True; if the user chooses Cancel to escape from the dialog box without making or saving any changes, Execute returns False.

3.	If the user doesn't cancel the dialog box, read and use the summary property value or option settings.

The following topics give examples of handling Windows common dialog boxes. The example code is from the sample MDI text editor application located in DEMOS\DOC\TEXTEDIT.


Selecting files to open

Section topics	See also

An MDI application can provide the ability to open existing files in a child window by interacting with the Open dialog box to load a file. By setting initial values for several properties of the open dialog component, and writing a few lines of code, you can provide users with an interface consistent with other Windows-based applications, so they can easily view a list of available files and select a file to open.


Specifying file filters

Section topics	See also

The List Files Of Type list box in the File|Open dialog box determines which types of files the dialog box displays. These file extensions are known as filters. If your application uses files with a certain pattern to their names, you can add custom filters to the standard File|Open dialog box.

To create a list of file-name filters:

1.	Select the Filters property for the Open dialog box in the Object Inspector.

2.	Click the ellipsis (...) button or double click the value column to open the Filter Editor.

3.	Type the name and filter mask for each type of file to filter.

If you need more information about creating file filters, see the online Help topics "Filter editor" and "Filter property."

You can also use the Options property to specify other behaviors in the dialog box, such as how it handles read-only files.


Opening an existing file

Section topics	See also

Applications generally open files in response to a menu command, but they sometimes use other mechanisms. The general procedure, however, is the same: In response to the menu command, the application:

1.	Has the user choose a file name with the File|Open dialog box.

2.	Reads the chosen file name and opens the specified file.

The following code from the sample text editor application shows event handlers for the File|Open click events in both the frame form and the child form. The child form's handler just calls the frame form's handler. Then, the FrameForm File|Open event handler calls an Open method attached to the child form.

procedure TFrameForm.Open1Click(Sender: TObject);

begin

 if OpenFileDialog.Execute then	{ if the user chose a file... }

 with TEditForm.Create(Self) do	{ create a new child window... }

   Open(OpenFileDialog.FileName);	{ ...and open the file }

end;

procedure TEditForm.Open1Click(Sender: TObject);

begin

 FrameForm.Open1Click(Sender);	 { call the handler in the frame form }

end;

The event handler code for the File|Open click executes the Open dialog box. When the user chooses a file to open, Execute returns True, and the remaining code runs. The file name specified by the user in the Open File dialog box is then passed to the child form's Open method.


Setting the cursor position upon opening a text file

Section topics	See also

There are a number of times when you might need to add methods to forms. One is when you want to add a capability to a type of form.

To add a method to a form:

1.	Add the method declaration to the form's class declaration.

2.	Write the method implementation in the implementation part of the form's unit.

You can find more information on writing methods in "Writing a procedure or function."

Here's an example from the sample text editor application that adds an Open method to a child form, so that the cursor will appear at the top of a document when it is opened. The following line is added in the public part of the TEditForm type declaration in the interface part of the MDIEdit unit:

procedure Open(const FileName: string);

The Open method appears in the implementation part:

procedure TEditForm.Open(const FileName: string);

begin

 PathName := FileName;	{ assign the parameter to the form variable }

 Editor.Lines.LoadFromFile(PathName);	{ load the file specified }

 Editor.SelStart := 0;	{ move cursor to top of file }

 Caption := ExtractFileName(PathName);	{ show file name in the form caption }

 Editor.Modified := False;	{ reset the Modified flag }

end;

Once the editor loads the file, the Open method resets the value of the editor's SelStart property to zero. The SelStart property represents the starting position of selected text, or the current cursor position if there is no text selected. By default, when the editor loads its contents from a file, SelStart is the end of the last line. By setting SelStart to zero, the cursor is made to appear where the user expects it, at the beginning of the file.

Setting the editor's Modified property to False enables tracking of any changes the user makes to the text file; if there are changes, Modified will be True. This is used later to prompt the user to save changed files before closing.


Saving files

Section topics	See also

Once users have created a new file, or modified an existing one, your application should enable them to save their work. The application needs to be able to save both existing files and new or unnamed files.

Saving a file involves event handlers for these instances:

When the user chooses File|Save

When the user chooses File|Save As


Saving existing files

Section topics	See also

The simplest case of saving is resaving an existing file. When the user chooses File|Save, the application can just write the file to the same place it loaded from. The only catch is if the user tries to save a new file without naming it, so you should check that before saving.

To save a file in the sample text editor application, the code calls the SaveToFile of the Editor component's Lines property. The Editor stores its lines in a string-list object, and string lists have a built-in mechanism for saving to files. For more information on string lists and files, see "Loading and saving string lists."

This code from the sample application attaches the following handler to the OnClick event for the child form's File|Save menu command:

procedure TEditForm.Save1Click(Sender: TObject);

begin

 if PathName = DefaultFileName then	{ if the file doesn't have a name yet... }

   SaveAs1Click(Sender)	{ ...choose a name for it }

 else	 { otherwise, }

 begin

   Editor.Lines.SaveToFile(PathName);	{ save to the named file }

   Editor.Modified := False;	{ reset Modified flag }

 end;

end;

Save1Click determines whether the file has been previously saved by checking for a non-default value of PathName. The application assigns a value to PathName when the user opens an existing file. Otherwise, PathName retains the default name. If the file still has the default name, Save1Click calls SaveAs1Click (which you'll write next) to get a name for the file. When the file has a name, Save1Click writes the Editor contents to that file and resets the Modified flag.


Saving a new or unnamed file

Section topics	See also

When saving a file for the first time (or saving an existing file to a different path), an application needs to prompt the user for a file name. That's the purpose of the save file dialog box.

The sample text editor application has a save file dialog box named SaveFileDialog, and this event handler is attached to the EditForm File|Save As click event:

procedure TEditForm.SaveAs1Click(Sender: TObject);

begin

 SaveFileDialog.FileName := PathName;	{ set the summary property }

 if SaveFileDialog.Execute then	{ choose a name... }

 begin	{ ...but only use it if OK }

   FileName := SaveFileDialog.FileName;	{ copy the summary property value }

   Caption := ExtractFileName(FileName);	{ show just the name part in the caption }

   Save1Click(Sender);	{ and save the file }

 end;

end;

If the file has already been named when the user selects Save As, SaveAs1Click displays the existing name in the File Name field of the file save dialog box. Otherwise, what's displayed there corresponds to the Filters property of the dialog box.

Note:	The user must enter a file name or the Save As dialog box does not close when the user chooses OK. This is a function of the dialog box, not of the event handler code.

The application can now save either new or existing files. Note that the saving methods call each other. Saving an unnamed file is really a "Save As," and once the file name is set, "Save As" just becomes "Save."


Creating a backup file

Section topics	See also

It's often a good idea to create a backup copy of the original file before saving a new version. You can add that to the file-saving method with just a few extra lines of Object Pascal code.

To create a backup file when saving:

1.	Delete any existing backup files.

2.	Rename the existing file to the backup name (usually changing the extension).

3.	Save the modified file to the original file name.

For example, a few lines added to the Save1Click method from the previous code example creates a backup file when saving:

procedure TEditForm.Save1Click(Sender: TObject);

var
  BackupFileName: string;	 { declare a holder for the backup file name }

const
  BackupExt = '.BAK';	{ define the backup file extension }

begin

 if PathName = DefaultFileName then
    SaveAs1Click(Sender)

 else

 begin

   BackupFileName := ChangeFileExt(PathName, BackupExt);	{ create backup name }

   DeleteFile(BackupFileName);	{ delete any existing backup }

   RenameFile(PathName, BackupFileName);	{ rename original file to backup... }

   Editor.Lines.SaveToFile(PathName);	{ ...and save under real name }

   Editor.Modified := False;

 end;

end;

Many applications, including Delphi itself, modify the existing file-name extension, rather than simply substituting a standard extension, such as .BAK. You could easily add this to your application.


Printing text

Section topics	See also

Printing is completely under the control of your application. Windows provides common dialog boxes for setting up the printer and for presenting the user with common printing options, but it is up to your application to respond to those options and render the printing to the printer.

Delphi provides a Printer object that encapsulates most printer behavior, which greatly simplifies your interactions with printing.

To use the printer object:

1.	Add the Printers unit to the uses clause of any unit that needs printer access.

2.	Write text or graphics to the canvas of the Printer object.


Setting up the printer

Section topics	See also

The Printer Setup dialog box is one of the Windows common dialog boxes. Unlike the other common dialog boxes, however, there is relatively little interaction between your application and the dialog box. The Printer Setup dialog box interacts directly with the settings in the Windows configuration files, so Windows itself handles the setting up of the printer.

To add printer-setup capability to your application:

1.	Place a printer setup dialog box component on a form.

2.	Execute the printer setup dialog box, for example,

PrinterSetupDialog1.Execute;

ChangeDefault is the only property available at design time for the Printer Setup dialog box component that interacts with the dialog box in the running application. The run-time value of this property changes to True when the user selects a different printer setup.

You don't need to check the result of calling Execute in this case, since the application isn't going to do anything different based on the user accepting or canceling the dialog box.


Printing the text

Section topics	See also

In general, printing in Windows involves rendering a graphical representation of data on the drawing surface of the printer. Using the same techniques, you can render text as graphics on the printer, but Delphi provides some shortcuts for printing text.

To print text from an application:

1.	Place a Print dialog box on a form.

2.	Execute the Print dialog at runtime to allow the user to set printing options.

3.	Send text to the printer.


Printing rich text

Section topics	See also

If you use a rich edit component, printing its contents is a simple, one-step procedure.

To print the contents of a rich edit component,

Call its Print method.

The Windows rich-text editor prints directly to the Windows default printer, bypassing the Delphi printer object entirely, but it renders all the text formatting and word wrapping for you. Print takes a single string as a parameter, which the document uses as its caption.

This example uses PathName as the name of the file being printed. It attaches a handler to the File|Print menu command in the child window:

procedure TEditForm.Print1Click(Sender: TObject);

begin

 if PrintDialog1.Execute then	{ execute the Print dialog box }

   Editor.Print(PathName);	{ tell the rich-text Editor to print itself }

end;

This method of printing ignores all the options in the Print dialog box entirely, although you can still use it to select a different printer or cancel printing.

The next topic shows how to print text to the printer directly.


Using the printer as a text device

Section topics	See also

In addition to the standard Windows printing interface, Delphi enables you to assign the printer as a standard text-file device, so you can write strings to it as if it were a text file. Printing this way is not fancy, but is an easy way to handle basic text printing.

To use the printer as a text-file device:

1.	Assign a text-file variable to the printer.

2.	Open the text file for writing.

3.	Write text to the file.

4.	Close the file.

The Delphi runtime library provides several standard procedures for text-file output. The most common ones are listed in the following table.

Routine	Purpose
Assign	Associates a text-file variable with a disk file.
AssignPrn	Associates a text-file variable with the printer device. This is similar to calling Assign to associate a text file with a file on a disk.
CloseFile	Closes a file opened with Reset or Rewrite.
Rewrite	Opens a text file for output.
Write	Writes data to the text file.
Writeln	Writes data to the text file, followed by a line break.

This example follows the steps for using the printer as a text-file device, as shown in the handler for the OnClick event for the File|Print menu item:

procedure TEditForm.Print1Click(Sender: TObject);

var
  Line: Integer;	{declare a line counter }

 PrintText: TextFile;	{ declare a text-file variable }

begin

 if PrintDialog1.Execute then

 begin

   AssignPrn(PrintText);	{ associate text file to printer device }

   Rewrite(PrintText);	{ create and open output file }

   try

     for Line := 0 to Editor.Lines.Count - 1 do	{ iterate the lines }

       Writeln(PrintText, Editor.Lines[Line]);	{ write each line to printer }

   finally
      CloseFile(PrintText);	{ close output file when done }

   end;

 end;

end;

Note the use of a try..finally block around the file-writing code. The finally part ensures that the file closes, even if an exception occurs when writing to the printer.


Setting the printer font

Section topics	See also

When an application uses the printer as a text device, text appears in whatever font the printer uses by default, which is the system font. In most cases, you'll want to set another font.

The printer font is a property of the printer's canvas. A canvas is a drawing surface on a Delphi component, which corresponds to a Windows device context.

To change the font for text printing,
 Set the Font property of the printer object's canvas.

Following on the previous text editor example, you would add the following line to the Print1Click handler, just below try:

Printer.Canvas.Font := Editor.Font	{ copy Editor font to printer }


Printing graphics

Section topics	See also

Printing graphic images from a Delphi application is a simple task. As for text, the only requirement for printing is that you add the Printers unit to the uses clause of the form that will call the printer. The Printers unit declares a printer object called Printer that has a canvas that represents the printed page.

To print a graphic image,
Copy the image to the printer's canvas.

You can use the printer's canvas just as you would any other canvas. In particular, that means you can copy the contents of a graphic object, such as a bitmap, to the printer directly.The following code copies the contents of an image control named Image to the printer in response to a click on a Print menu item:

procedure TForm1.Print1Click(Sender: TObject);
begin
  with Printer do
  begin
    BeginDoc;	{ start printing }

   Canvas.Draw(0, 0, Image.Picture.Graphic);	{ draw Image at upper left of page }

   EndDoc;	{ finish printing }
  end;
end;


Using the font dialog box

Section topics	See also

At design time, you use the Font property of an edit component to set an initial font for text in the component. You can also provide a way for the user to choose a different font at runtime. The Windows Font dialog box provides a standard way for users to choose or modify fonts, tailored to the runtime environment.

The following example, from the sample text editor application, attaches a handler to the Character|Font OnClick event:

procedure TEditForm.Font1Click(Sender: TObject);

begin

 FontDialog1.Font := Editor.Font;	{ set the summary property }

 if FontDialog1.Execute then	{ execute the dialog box }

   Editor.Font := FontDialog1.Font;	{ read the summary property value }
end;

This event handler changes the font in the edit component (Editor) when the user chooses Character|Font and makes a change. The change affects all the text in Editor.


Changing the selected-text font in a rich-text edit component

Section topics	See also

In a rich edit component, you can change font attributes for just the selected portion of the text.

To modify font attributes of selected text in a rich edit component,
 Assign changes to the edit component's SelectedText property instead of the Font property.

For example, the following code modifies the Font1Click handler in the sample text editor application to change only the selected portions of the text with only a minor change:

procedure TEditForm.Font1Click(Sender: TObject);

begin

 FontDialog1.Font := Editor.Font;	{ set the summary property }

 if FontDialog1.Execute then	{ execute the dialog box }

   Editor.SelectedText.Assign(FontDialog1.Font);	{ read the summary property value }

end;	{ use Assign instead of := here }


See also

Dialogs components

TPrinter


Working with toolbars and cool bars

Section topics	See also

A toolbar is a panel, usually across the top of a form (under the menu bar), that holds buttons and other controls. Toolbars provide a visible way to present options to the user, and Delphi makes it easy to add toolbars to your forms. A cool bar is a kind of toolbar that displays controls on movable, resizable bands.

You can add as many toolbars to a form as you want. If you have multiple panels aligned to the top of the form, they stack vertically in the order added.

These topics explain the advantages of toolbars and cool bars, and they cover how to:

Add a toolbar or cool bar to a form

Populate the bar with buttons

Add glyphs to the buttons

Hide and show toolbars and cool bars


Different kinds of toolbar control

Section topics	See also

There are several ways to add a "toolbar" to a form:

Place a panel (TPanel) on the form and add controls (typically speed buttons) to it.

Use a toolbar component (TToolBar) instead of TPanel, and add controls to it. TToolBar manages buttons and other controls, arranging them in rows and automatically adjusting their sizes and positions. If you use tool button (TToolButton) controls on the toolbar, TToolBar makes it easy to group the buttons functionally and provides other display options.

Use a cool bar (TCoolBar) component and add controls to it. The cool bar displays controls on independently movable and resizable bands.


Adding a toolbar using a panel component

Section topics	See also

To add a toolbar to a form using the panel component:

1.	Add a panel component to the form (from the Standard page of the Component palette).

2.	Set the panel's Align property to alTop. When aligned to the top of the form, the panel maintains its height, but matches its width to the full width of the form's client area, even if the window changes size.

3.	Add speed buttons or other controls to the panel.


Adding speed buttons to a toolbar panel

Section topics	See also

Delphi provides special buttons, called speed buttons, designed to work on toolbar panels. A speed button usually has no caption, only a small graphic (called a glyph) which represents the button's function.

Speed buttons have three possible modes of operation:

Act like regular pushbuttons

Toggle on and off when clicked

Act like a set of radio buttons

There are four tasks related to speed buttons on toolbars:

Adding a speed button to a panel

Assigning a speed button's glyph

Setting the initial condition of a speed button

Creating a group of speed buttons

Adding a speed button to a panel

To add a speed button to a toolbar panel,
Place the speed button component (from the Additional page of the Component palette) on the panel.

The panel, rather than the form, "owns" the speed button, so moving or hiding the panel also moves or hides the speed button.

The default height of the panel is 41, and the default height of speed buttons is 25. If you set the Top property of each button to 8, they'll be vertically centered. The default grid setting snaps the speed button to that vertical position for you.

Assigning a speed button's glyph

Each speed button needs a graphic image called a glyph to indicate to the user what the button does. If you supply the speed button only one image, the button manipulates that image to indicate whether the button is pressed, unpressed, selected, or disabled. You can also supply separate, specific images for each state if you prefer.

You normally assign glyphs to speed buttons at design time, although you can assign different glyphs at runtime.

To assign a glyph to a speed button at design time:

1.	Select the speed button.

2.	In the Object Inspector, select the Glyph property.

3.	Double-click the Value column beside Glyph.

Delphi opens the Picture editor.

4.	Choose Load in the Picture editor.

Delphi opens a file-open dialog box.

5.	Select the .BMP file that contains the image you want on the button, and choose OK.

The file-open dialog box closes, and the Picture editor displays the selected bitmap image.

6.	Choose OK to close the Picture editor.

Setting the initial condition of a speed button

Speed buttons use their appearance to give the user clues as to their state and purpose. Because they have no caption, it's important that you use the right visual cues to assist users.

To make a speed button appear pressed,
 Set its GroupIndex property to a nonzero value and its Down property to True.

To make a speed button appear disabled,
Set its Enabled property to False.

Creating a group of speed buttons

A series of speed buttons often represents a set of mutually exclusive choices. In that case, you need to associate the buttons into a group, so that clicking any button in the group causes the others in the group to pop up.

To associate any number of speed buttons into a group,
Assign the same number to each speed button's GroupIndex property.

The easiest way to do this is to select all the buttons you want in the group, and, with the whole group selected, set GroupIndex to a unique value.


Allowing toggle buttons

Section topics	See also

Sometimes you want to be able to click a button in a group that's already pressed and have it pop up, leaving no button in the group pressed. Such a button is called a toggle. Use AllowAllUp to create a grouped button that acts as a toggle: click it once, it's down; click it again, it pops up.

To make a grouped speed button a toggle,
Set its AllowAllUp property to True.

Setting AllowAllUp to True for any speed button in a group automatically sets the same property value for all buttons in the group. This enables the group to act as a normal group, with only one button pressed at a time, but also allows every button to be up at the same time.


Adding a toolbar using the toolbar component

Section topics	See also

The toolbar component (TToolBar) offers button management and display features that panel components do not.

To add a toolbar to a form by using the toolbar component:

1.	Add a toolbar component to the form (from the Win32 page of the Component palette). The toolbar automatically aligns to the top of the form.

2.	Add tool buttons or other controls to the bar.


Adding tool buttons to a toolbar

Section topics	See also

Delphi provides special buttons, called tool buttons, designed to work on toolbar components. Like speed buttons, tool buttons can

Act like regular pushbuttons

Toggle on and off when clicked

Act like a set of radio buttons

Adding a tool button to the toolbar

To add a tool button to a toolbar,
Right-click on the toolbar and choose New Button from the context menu.

The toolbar "owns" the tool button, so moving or hiding the toolbar also moves or hides the button. In addition, all tool buttons on the toolbar automatically maintain the same height and width. You can drop other controls from the Component palette onto the toolbar, and they will automatically maintain a uniform height. Controls will also wrap around and start a new row when they do not fit horizontally on the toolbar.

Assigning images to tool buttons

Each tool button has an ImageIndex property that determines what image appears on it at runtime. If you supply the tool button only one image, the button manipulates that image to indicate whether the button is disabled.

To assign images to tool buttons at design time:

1.	Select the toolbar on which the buttons appear.

2.	In the Object Inspector, assign a TImageList object to the toolbar's Images property. An image list is a collection of same-sized icons or bitmaps.

3.	Select a tool button.

4.	In the Object Inspector, assign an integer to the tool button's ImageIndex property that corresponds to the image in the image list that you want to assign to the button.

You can also specify separate images to appear on the tool buttons when they are disabled and when they are under the mouse pointer. To do so, assign separate image lists to the toolbar's DisabledImages and HotImages properties.

Setting tool buttons' appearance and initial conditions

To make a tool button appear pressed,
 Set its GroupIndex property to a nonzero value. Set its Down property to True.

To make a tool button appear disabled,
 Set its Enabled property to False.

To create a margin between the left edge of the toolbar and the controls within it,
 Set the toolbar's Indent property to a value greater than 0.

To make the toolbar transparent and give the tool buttons "pop-up" borders,
 Set the toolbar's Flat property to True.

Note:	Using the Flat property of TToolBar requires version 4.70 or later of COMCTL32.DLL.

To force a new row of controls after a specific tool button,
 Select the tool button that you want to appear last in the row and set its Wrap property to True.

To turn off the auto-wrap feature of the toolbar,
 Set the toolbar's Wrapable property to False.

Creating groups of tool buttons

To create a group of tool buttons,
Select the buttons you want to associate and set their Style property to tbsCheck; then set their Grouped property to True. Selecting a grouped tool button causes other buttons in the group to pop up.

Any unbroken sequence of adjacent tool buttons with Style set to tbsCheck and Grouped set to True forms a single group.

To break up a group of tool buttons, separate the buttons with any of the following:

A tool button whose Grouped property is False.

A tool button whose Style property is not set to tbsCheck. (To create spaces or dividers on the toolbar, add a tool button whose Style is tbsSeparator or tbsDivider.)

Another control besides a tool button.

Allowing toggled tool buttons

Use AllowAllUp to create a grouped tool button that acts as a toggle: click it once, it's down; click it again, it pops up.

To make a grouped tool button a toggle,
Set its AllowAllUp property to True.

As with speed buttons, setting AllowAllUp to True for any tool button in a group automatically sets the same property value for all buttons in the group.


Adding a cool bar component

Section topics	See also

The cool bar component--also called a rebar--displays windowed controls on independently movable, resizable bands. The user can position the bands by dragging the resizing grips on the left side of each band.

To add a cool bar to a form:

1.	Add a cool bar component to the form (from the Win32 page of the Component palette). The cool bar automatically aligns to the top of the form.

2.	Add windowed controls from the Component palette to the bar.

Only components that descend from TWinControl are windowed controls. You can add graphic controls--such as labels or speed buttons--to the cool bar, but they will not appear on separate bands.

Note:	The cool bar component requires version 4.70 or later of COMCTL.DLL.


Configuring the cool bar

Section topics	See also

The cool bar component offers several useful configuration options:

Set the AutoSize property to True if you want the cool bar to resize automatically to accommodate the bands it contains.

Set FixedSize to True if you want the bands to maintain a uniform height.

Set Vertical to True to reorient the cool bar so that the bands are arranged vertically. (The FixedSize property will now have a different effect.)

Each band (TCoolBand) in a cool bar has its own Text property. To prevent the Text properties of the bands from displaying at runtime, set the cool bar's ShowText property to False.

Set BandBorderStyle to bsNone to remove the border around the bar.

Set FixedOrder to True to keep users from changing the bands' order at runtime. (The user can still move and resize the bands.)

Assign a TBitmap object to the Bitmap property to create a background image for the cool bar.

Assign a TImageList object to the Images property to choose a list of images that can appear to the left of any band. To assign images to individual bands, select the cool bar and double-click on the Bands property in the Object Inspector. Then select a band and assign a value to its ImageIndex property.


Responding to clicks

Section topics	See also

When the user clicks a control, such as a button on a toolbar, the application generates an OnClick event. You handle these OnClick events by writing OnClick event handlers for your forms.


Writing an event handler for a button click

Section topics	See also

Button controls, including speed buttons and tool buttons, have OnClick events built into them.

To respond to the user's click on a button,
Define a handler for the button's OnClick event.

When you double-click on a button in the Form designer, Delphi generates an OnClick event handler named after the control clicked. For example, the default OnClick event handler for a speed button named LineButton would be

procedure TForm1.LineButtonClick(Sender: TObject);
begin

end;

You then fill in the action you want to occur when the user clicks that button.


Assigning a menu to a tool button

Section topics	See also

If you are using a toolbar (TToolBar) with tool buttons (TToolButton), there is an easy way to associate a menu with a specific button:

1.	Select the tool button.

2.	In the Object Inspector, assign a pop-up menu (TPopupMenu) to the tool button's DropdownMenu property.

If the menu's AutoPopup property is set to True, it will appear automatically when the button is pressed.


Adding hidden toolbars

Section topics	See also

Toolbars don't have to be visible all the time. In fact, it's often convenient to have a number of toolbars available, but show them only when the user wants to use them. Often you create a form that has several toolbars, but hide some or all of them.

To create a hidden toolbar:

1.	Add a toolbar, cool bar, or panel component to the form.

2.	Set the component's Visible property to False.

Although the toolbar remains visible at design time so you can modify it, it remains hidden at runtime until the application specifically makes it visible.


Hiding and showing toolbars

Section topics	See also

Often, you want an application to have multiple toolbars, but you don't want to clutter the form with all of them at once. Or you may want to let users decide whether to display toolbars. As with all components, toolbars can be shown or hidden at runtime as needed.

To hide or show a toolbar at runtime,
Set its Visible property to False or True, respectively.

Usually you do this in response to particular user events or changes in the operating mode of the application.

A common practice is to put some kind of close button on each toolbar. When the user clicks that button, the application hides the corresponding toolbar. You can also provide a means of toggling the toolbar. In the following example, a toolbar of pens is toggled from a button on the main toolbar.


See also

Standard components

Win32 components

ToolBar

CoolBar


Dragging and dropping

Section topics	See also

Dragging and dropping of items can be a handy way to enable users to manipulate objects in a form. You can let users drag entire components, or let them drag items out of components such as list boxes into other components.

There are four essential elements to drag-and-drop operations:

1.	Starting a drag operation

2.	Accepting dragged items

3.	Dropping items

4.	Ending a drag operation


Starting a drag operation

Section topics	See also

Every control has a property called DragMode that controls how the component responds when a user begins dragging the component at runtime. If DragMode is dmAutomatic, dragging begins automatically when the user presses a mouse button with the cursor on the control. A more common usage is to set DragMode to dmManual (which is the default) and start the dragging by handling mouse-down events.

To start dragging a control manually,
Call the control's BeginDrag method.

BeginDrag takes a Boolean parameter called Immediate. If you pass True, dragging begins immediately, much as if DragMode were dmAutomatic. If you pass False, dragging doesn't begin until the user actually moves the mouse a short distance. Calling BeginDrag(False) allows the control to accept mouse clicks without beginning a drag operation.

You can also place conditions on whether to begin dragging, such as checking which button the user pressed, by testing the parameters of the mouse-down event before calling BeginDrag.

Example

The following code handles a mouse-down event on a file list box by beginning dragging only if the left mouse button was pressed:

procedure TFMForm.FileListBox1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

begin

 if Button = mbLeft then	{ drag only if left button pressed }

   with Sender as TFileListBox do	{ treat Sender as TFileListBox }
    begin

     if ItemAtPos(Point(X, Y), True) >= 0 then	{ is there an item here? }
        BeginDrag(False);	{ if so, drag it }

   end;
end;


Accepting dragged items

Section topics	See also

When a user drags something over a control, that control receives an OnDragOver event, at which time it must indicate whether it can accept the item if the user drops it there. Delphi changes the drag cursor to indicate whether the control can accept the dragged item.

To accept items dragged over a control,
Attach an event handler to the control's OnDragOver event.

The drag-over event has a variable parameter called Accept that the event handler can set to True if it will accept the item.

Setting Accept to True specifies that if the user releases the mouse button at that point, dropping the dragged item, the application can then send a drag-drop event to the same control. If Accept is False, the application won't drop the item on that control. This means that a control should never have to handle a drag-drop event for an item it doesn't know how to handle.

The drag-over event includes several parameters, including the source of the dragging and the current location of the mouse cursor. The event handler can use those parameters to determine whether to accept the drop. Most often, a control accepts or rejects a dragged item based on the type of the sender, but it can also accept items only from specific instances.

Example

In the following example, a directory outline accepts dragged items only if they come from a file list box:

procedure TFMForm.DirectoryOutline1DragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);

begin
  if Source is TFileListBox then
    Accept := True;

  else

     Accept := False;
end;


Dropping items

Section topics	See also

Once a control indicates that it can accept a dragged item, it should then also define some way to handle the item should it be dropped. If a user sees the mouse cursor change to indicate that a control will accept the item being dragged, it is reasonable for the user to then expect that dropping the item there will accomplish some task.

To handle dropped items,
 Attach an event handler to the OnDragDrop event of the control accepting the dropped item.

Like the drag-over event, the drag-drop event indicates the source of the dragged item and the coordinates of the mouse cursor over the accepting control. These parameters enable the drag-drop handler to get any needed information from the source of the drag and determine how to handle it.

Example

For example, a directory outline accepting items dragged from a file list box can move the file from its current location to the directory dropped on:

procedure TFMForm.DirectoryOutline1DragDrop(Sender, Source: TObject; X,
  Y: Integer);
begin
  if Source is TFileListBox then

   with DirectoryOutline1 do
      ConfirmChange('Move', FileList.FileName, Items[GetItem(X, Y)].FullPath);
end;

Dragging and dropping now has the same effect as using File|Move, but the user doesn't need to type in the file name.


Ending a drag operation

Section topics	See also

When a dragging operation ends, either by dropping the dragged item or by the user releasing the mouse button over a control that doesn't accept the dragged item, Delphi sends an end-drag event back to the control the user dragged.

To enable a control to respond when items have been dragged from it,
 Attach an event handler to the OnEndDrag event of the control.

The most important parameter in an OnEndDrag event is called Target, which indicates which control, if any, accepts the drop. If Target is nil, it means no control accepts the dragged item. Otherwise, Target is the control that accepts the item. The OnEndDrag event also includes the x- and y-coordinates on the receiving control where the drop occurs.

Example

In this example, a file list box handles an end-drag event by refreshing its file list, assuming that dragging a file from the list changes the contents of the current directory:

procedure TFMForm.FileList1EndDrag(Sender, Target: TObject; X, Y: Integer);
begin
  if Target <> nil then FileList1.Update;
end;


See also

TListBox

TComboBox

TDBListBox

TDBComboBox

TDBLookupListBox

TDBLookupComboBox


Working with text

Section topics	See also

If you need to provide options like setting text alignment; word wrapping; cutting, copying, and pasting; and deleting text--all of which you'll want in a rich edit or memo component--you can attach event handlers to a form's menu items or to buttons. You also need to enable the Clipboard object, and you can add a local, or pop-up, menu for frequently used commands such as Cut, Copy, and Paste. Finally, you can use the Windows font dialog box to change font attributes.

These topics explain how to add these features to a rich edit or memo component. Some of the features work with an edit component, as well.


Setting text alignment

Section topics	See also

In a rich edit or memo component, users can align text to either the right or left margin or center the text between the two. In the sample text editor application (in the \DEMOS\DOC directory), a check mark on the menu indicates which option is in effect.

Note that alignment takes effect only if the WordWrap property is True. If word wrapping is turned off, there is no right margin to align to.

To set text alignment in an edit component,

Set the edit component's Alignment property.

In the sample text editor application, text-alignment commands on the child form's Character menu share a single event handler that uses the Sender parameter to check which alignment option was selected. Depending on the value of Sender, the event handler sets the appropriate alignment, and either checks or unchecks the menu items as called for.

For example, the following code attaches an OnClick event handler to the Character|Left menu item, then attaches the same event handler to both the Right and Center menu items on the Character menu.

procedure TEditForm.AlignClick(Sender: TObject);

begin
  Left1.Checked := False;	{ clear all three checks }
  Right1.Checked := False;
  Center1.Checked := False;

 with Sender as TMenuItem do Checked := True;	{ check the item clicked }

 with Editor do	{ then set Alignment to match }

   if Left1.Checked then

     Alignment := taLeftJustify

   else if Right1.Checked then

     Alignment := taRightJustify

   else if Center1.Checked then

     Alignment := taCenter;

end;

For more information on sharing event handlers, see "Associating an event with an existing event handler."


Adding scroll bars at runtime

Section topics	See also

Rich edit and memo components can contain horizontal or vertical scroll bars, or both, as needed. When word wrapping is enabled, the component needs only a vertical scroll bar. If the user turns off word-wrapping, the component might also need a horizontal scroll bar, since text will not be limited by the right side of the editor.

To add scroll bars at runtime:

1.	Determine whether the text might exceed the right margin.

In most cases, that means checking whether the rich edit or memo component has word wrapping enabled. You might also check whether any text lines actually exceed the width of the rich edit or memo component.

2.	Set the rich edit or memo component's ScrollBars property to include or exclude scroll bars.

The following example attaches an OnClick event handler to a Character|WordWrap menu item. In addition to toggling the Editor component's WordWrap property, the OnClick event handler sets the ScrollBars property to a value appropriate to the current word-wrapping:

procedure TEditForm.WordWrap1Click(Sender: TObject);

begin

 with Editor do

 begin

   WordWrap := not WordWrap;	{ toggle word-wrapping }

   if WordWrap then

     ScrollBars := ssVertical	{ wrapped requires only vertical }

   else

     ScrollBars := ssBoth;	{ unwrapped might need both }

   WordWrap1.Checked := WordWrap;	{ check menu item to match property }

 end;

end;

The rich edit and memo components handle their scroll bars slightly differently. The rich edit component can hide its scroll bars if the text fits inside the bounds of the component. The memo always shows scroll bars if they are enabled.


Adding the Clipboard object

Section topics	See also

Most text handling applications provide users with a way to move selected text between documents, including documents in different applications. The Clipboard object in Delphi encapsulates the Windows Clipboard and includes methods for cutting, copying, and pasting text (and other formats, including graphics). The Clipboard object is declared in the Clipbrd unit.

To add the Clipboard object to an application:

1.	Select the unit that will use the Clipboard.

2.	Search for the implementation reserved word.

3.	Add Clipbrd to the uses clause below implementation.

If there is already a uses clause in the implementation part, add Clipbrd to the end of it.

If there is not already a uses clause, add one that says

  uses Clipbrd;

Using units and uses clauses are explained in more detail in "Writing Object Pascal code."

For example, in an application with a child window, the uses clause in the unit's implementation part might look like this:

uses
  MDIFrame, Clipbrd;


About selected text

Section topics	See also

Before you can send any text to the Clipboard, that text must be selected. Highlighting selected text is built into the edit components; you don't need to write code so that an edit component can display selected text. When the user selects text, it will appear highlighted. There are also several ways to select text and manipulate any selected text using code.

Edit components have several properties and methods that work with selected text. SelText, a run-time only property, contains a string representing the selected text in the component. The SelectAll method selects all the text in the edit component. The SelLength and SelStart methods return values for a selected string's length and starting position, respectively.

SelLength is used in the example in "Disabling menu items." SelStart is discussed in "Opening an existing file."


Selecting text

Section topics	See also

The most common task for selecting text in code is to provide a way for the user to select the entire contents of the rich edit or memo component. This is especially useful when the component's contents exceed the visible area of the component. In most other cases, users select text with either keystrokes or mouse dragging.

To select the entire contents of a rich edit or memo component,

Call the editor's SelectAll method.

For example,

procedure TEditForm.SelectAll(Sender: TObject);

begin
  Editor.SelectAll;	{ select all text in the Editor component }

end;


Cutting, copying, and pasting text

Section topics	See also

Delphi applications that use the Clipbrd unit can cut, copy, and paste text, graphics, and objects both internally and with other applications through the Windows Clipboard. The edit components that encapsulate the standard Windows text-handling controls all have methods built into them for interacting with the Clipboard. (See "Working with graphics files" for information on using the Clipboard with graphics.)

To cut, copy, or paste text with the Clipboard,

Call the edit component's CutToClipboard, CopyToClipboard, and PasteFromClipboard methods, respectively.

For example, the following code attaches event handlers to the OnClick events of the Edit|Cut, Edit|Copy, and Edit|Paste commands, respectively:

procedure TEditForm.CutToClipboard(Sender: TObject);

begin

 Editor.CutToClipboard;

end;

procedure TEditForm.CopyToClipboard(Sender: TObject);

begin

 Editor.CopyToClipboard;

end;

procedure TEditForm.PasteFromClipboard(Sender: TObject);

begin

 Editor.PasteFromClipboard;

end;


Deleting selected text

Section topics	See also

Selected text is often used with the Clipboard, but you can also write code to delete the selected text in an edit component without cutting it to the Clipboard.

To delete the selected text from an edit component,

Call the editor's ClearSelection method.

For example, if you have a Delete item on the Edit menu and an Editor component, your code would look something like this:

procedure TEditForm.Delete(Sender: TObject);

begin

 Editor.ClearSelection;

end;


Disabling menu items

Section topics	See also

Often in an application you want to disable menu commands that are not immediately applicable, but you don't want to remove the command from the menu altogether. For example, in a text editor, if there is no text currently selected, the Cut, Copy, and Delete items on the Edit menu appear dimmed. Disabling the item lets the user know there is such a command, but that it does not currently apply.

A good time to enable or disable items on a particular menu is when the user clicks the item at the top of the menu. That way, before displaying the items, your application can make the correct items available.

To disable a menu item,

Set its Enabled property to False.

In the following code example, an event handler is attached to the OnClick event for the Edit item on a child form's menu bar. It sets Enabled for the Cut, Copy, and Delete menu items on the Edit menu based on whether Editor has selected text. The Paste command is enabled or disabled based on whether any text exists on the Clipboard.

procedure TEditForm.Edit1Click(Sender: TObject);

var
  HasSelection: Boolean;	{ declare a temporary variable }

begin

 Paste1.Enabled := Clipboard.HasFormat(CF_TEXT);	{enable or disable the Paste menu item}

 HasSelection := Editor.SelLength > 0;	{ True if text is selected }

 Cut1.Enabled := HasSelection;	{ enable menu items if HasSelection is True }

 Copy1.Enabled := HasSelection;

 Delete1.Enabled := HasSelection;

end;

The HasFormat method of the Clipboard returns a Boolean value based on whether the Clipboard contains objects, text, or images of a particular format. By calling HasFormat with the parameter CF_TEXT, you can determine whether the Clipboard contains any text, and enable or disable the Paste item as appropriate.

For information about using the Clipboard object with graphics, see "Working with graphics files."


Providing a pop-up menu

Section topics	See also

Pop-up, or local, menus are a common ease-of-use feature for any application. They enable users to minimize mouse movement by clicking the right mouse button in the application workspace to access a list of frequently used commands.

In a text editor application, for example, you can add a pop-up menu that repeats the Cut, Copy, and Paste editing commands. These pop-up menu items can use the same event handlers as the corresponding items on the Edit menu. You don't need to create accelerator or shortcut keys for pop-up menus, because the corresponding regular menu items generally already have shortcuts.

A form's PopupMenu property specifies what pop-up menu to display when a user right-clicks any item on the form. Individual controls also have PopupMenu properties that can override the form's property, allowing customized menus for particular controls.

To add a pop-up menu to a form:

1.	Place a pop-up menu component on the form.

2.	Use the Menu Designer to define the items for the pop-up menu.

3.	Set the PopupMenu property of the form or control that will display the menu to the name of the pop-up menu component.

4.	Attach handlers to the OnClick events of the pop-up menu items.


Handling the OnPopup event

Section topics	See also

You will sometimes need to adjust pop-up menu items before displaying the menu, just as you will sometimes enable or disable items on a regular menu. With a regular menu, you can handle the OnClick event for the item at the top of the menu, as described in "Disabling menu items."

With a pop-up menu, however, there is no top-level menu bar, so to prepare the pop-up menu commands, you handle the event in the menu component itself. The pop-up menu component provides an event just for this purpose, called OnPopup.

To adjust menu items on a pop-up menu before displaying them:

1.	Select the pop-up menu component.

2.	Attach an event handler to its OnPopup event.

3.	Write code in the event handler to enable, disable, hide, or show menu items.

In the following code, an existing Edit1Click event handler is attached to the pop-up menu component's OnPopup event. A line of code is added to Edit1Click for each item in the pop-up menu.

procedure TEditForm.Edit1Click(Sender: TObject);

var
  HasSelection: Boolean;

begin

 Paste1.Enabled := Clipboard.HasFormat(CF_TEXT);

 Paste2.Enabled := Paste1.Enabled;	{Add this line}

 HasSelection := Editor.SelLength <> 0;

 Cut1.Enabled := HasSelection;

 Cut2.Enabled := HasSelection;	{Add this line}

 Copy1.Enabled := HasSelection;

 Copy2.Enabled := HasSelection;	{Add this line}

 Delete1.Enabled := HasSelection;

end;


See also

TRichEdit

TDBRichEdit

TMemo

TDBMemo

TClipboard

TMenuItem

TPopupMenu

Using the Menu designer


Drawing graphics at runtime

Section topics	See also

There are three ways to get graphic images into your Delphi applications: you can insert pre-drawn pictures at design time, create them using graphical controls at design time, or draw them dynamically at runtime. These topics describe how to draw graphics at runtime, either on a window or on a custom control or owner-draw control. Design-time graphics, using image controls, are explained in "Creating an owner-draw control."

When you draw graphics in a Delphi application, you draw on an object's canvas, rather than directly on the object. The canvas is a property of the object, and is itself an object with its own properties. The canvas object has four important properties: a pen for drawing lines, a brush for filling shapes, a font for writing text, and an array of pixels to represent the image.

The Font property is described in "Using Windows common dialogs." These topics discuss how to use the pixel array, the pen, and the brush.

Canvases are available only at runtime, so you do all your work with canvases by writing code. You'll find that the canvas is easier to use than direct calls to the Windows Graphics Device Interface (GDI), while maintaining a high degree of compatibility.

To use graphics effectively at runtime, you need to understand

Drawing versus painting

Using the pixel array

Drawing lines and polylines

Drawing shapes

Drawing special parts


Drawing versus painting

Section topics	See also

When working with graphics, you often encounter the terms drawing and painting. It's important to understand the difference between the two.

Drawing is the creation of a single, specific graphic element, such as a line or a shape, with code. In your code, you tell an object to draw a specific graphic in a specific place on its canvas by calling a drawing method of the canvas.

Painting is the creation of the entire appearance of an object. At certain times, Windows determines that objects onscreen need to refresh their appearance, so it generates OnPaint events. OnPaint events happen when, for example, a window that was hidden is brought to the front.

The thing to remember is this: painting usually involves drawing. That is, in response to OnPaint events, an object generally draws some graphics. An edit box, for example, paints itself by drawing a rectangle and then drawing some text inside. A shape control, on the other hand, paints itself by drawing a single graphic.

The examples in the first of these topics demonstrate how to draw various graphics, but they do so in response to OnPaint events. Later topics show how to do the same kind of drawing in response to other events.


Using the pixel array

Section topics	See also

Every canvas has an indexed property called Pixels that represents the individual colored points that make up the image on the canvas. You rarely need to access Pixels directly, unless you need to find out what color a particular pixel is or to set that pixel's color. It's more important to understand that Pixels is the drawing surface of the canvas, and that the pen and brush are just convenient ways to manipulate groups of pixels.

These topics describe:

Manipulating pixels

Using pens

Using brushes


Manipulating pixels

Section topics	See also

You can treat the pixels of a canvas like a two-dimensional array, with each element in the array having a particular color. You can either read or set individual pixels.

Reading a pixel's color

To retrieve the color of a particular pixel,

Access the Pixels property of the canvas, using as indexes the x- and y-coordinates of the pixel you want to read. The upper left corner of the canvas is the origin of the coordinate system.

For example, the following event handler responds to a click on a check box by changing the color of the text in the check box to the color of the pixel at the point (10, 10) in the form:

procedure TForm1.CheckBox1Click(Sender: TObject);

begin

 CheckBox1.Font.Color := Canvas.Pixels[10, 10];

end;

Setting a pixel's color

To set a pixel's color,

Assign a color value to the Pixels property, using as indexes the x- and y-coordinates of the pixel. The upper left corner of the canvas is the origin of the coordinate system.

For example, the following event handler responds to a click on a button by changing a random pixel on the form to red. (For detailed information on the Random function, see online Help.)

procedure TForm1.Button1Click(Sender: TObject);

begin

 Canvas.Pixels[Random(ClientWidth), Random(ClientHeight)] := clRed;

end;


Using pens

Section topics	See also

The Pen property of a canvas controls the way lines appear, including lines drawn as the outlines of shapes. Drawing a straight line is really just changing a group of pixels that lie between two points.

The pen itself has four properties you can change: Color, Width, Style, and Mode.

The values of these properties determine how the pen changes the pixels in the line. By default, every pen starts out black, with a width of 1 pixel, a solid style, and a mode called copy that overwrites anything already on the canvas.

You can set the color of a pen as you would any other Color property at runtime. Width is an integer number of pixels. Style gives you solid lines, dashed lines, dotted lines, and so on. Mode lets you specify various ways to combine the pen's color with the colors already in the pixel array.

See "Customizing pens and brushes" for more information.


Moving the pen

Section topics	See also

The current drawing position--the position from which the pen will draw its next line--is called the pen position. The canvas stores its pen position in a property called PenPos. Pen position affects the drawing of lines only; for shapes and text, you specify all the coordinates you need.

To set the pen position,

Call the MoveTo method of the canvas.

For example, the following code moves the pen position to the upper left corner of the canvas:

Canvas.MoveTo(0, 0);

Note:	Drawing a line with the LineTo method also moves the current position to the endpoint of the line.


Using brushes

Section topics	See also

The Brush property of a canvas controls the way you fill areas, including the interior of shapes. Filling an area with a brush is a way of changing a large number of adjacent pixels in a specified way.

The brush has three properties you can manipulate: Color, Style, and Bitmap.

The values of these properties determine the way the canvas fills shapes or other areas. By default, every brush starts out white, with a solid style and no pattern bitmap.

You can set the color of a brush as you would any other Color property at runtime. Style lets you specify various ways to combine the brush's color with any colors already on the canvas. Bitmap lets you specify a bitmap image for the brush to use as a pattern for filling shapes and other areas.

See "Customizing pens and brushes" for more information.


Drawing lines and polylines

Section topics	See also

A canvas can draw two kinds of lines: straight lines and polylines. A straight line is just a line of pixels connecting two points. A polyline is a string of straight lines, connected end-to-end. The canvas draws all lines using its pen.


Drawing straight lines

Section topics	See also

To draw a straight line on a canvas,

Use the LineTo method of the canvas.

LineTo draws a line from the current pen position to the point you specify and makes the endpoint of the line the current position. The canvas draws the line using its pen.

For example, the following method draws crossed diagonal lines across a form whenever the form is painted:

procedure TForm1.FormPaint(Sender: TObject);

begin

 with Canvas do

 begin

   MoveTo(0, 0);

   LineTo(ClientWidth, ClientHeight);

   MoveTo(0, ClientHeight);

   LineTo(ClientWidth, 0);

 end;

end;

An OnPaint event occurs whenever Windows determines that a form needs to be redrawn, such as when another window covers it and then goes away again. FormPaint is the default name Delphi generates for a handler for a form's OnPaint event. Handle OnPaint events when you want to paint a background on a form or when you want to regenerate an image.

The next few examples in these topics also do their drawing in OnPaint event handlers. "Techniques for drawing in an application" shows how to draw on a canvas interactively.


Drawing polylines

Section topics	See also

In addition to individual lines, the canvas can also draw polylines, which are groups of any number of connected line segments.

To draw a polyline on a canvas,

Call the PolyLine method of the canvas.

The parameter passed to the PolyLine method is an array of points. You can think of a polyline as performing a MoveTo on the first point and LineTo on each successive point.

The following method, for example, draws a rhombus in a form:

procedure TForm1.FormPaint(Sender: TObject);

begin

 with Canvas do

   PolyLine([Point(0, 0), Point(50, 0), Point(75, 50), Point(25, 50), Point(0, 0)]);

end;

This example takes advantage of Delphi's ability to create an open-array parameter on-the-fly. You can pass any array of points, but an easy way to construct an array quickly is to put its elements in brackets and pass the whole thing as a parameter. For more information, see online Help.


Drawing shapes

Section topics	See also

Canvases have methods for drawing four kinds of shapes. The canvas draws the outline of a shape with its pen, then fills the interior with its brush.

There are three distinct kinds of shapes to draw:

Rectangles and ellipses

Rounded rectangles

Polygons

Drawing rectangles, ellipses, and rounded rectangles are quite similar, but polygons are a little different.


Drawing rectangles and ellipses

Section topics	See also

To draw a rectangle or ellipse on a canvas,
Call the canvas's Rectangle or Ellipse method, passing the coordinates of a bounding rectangle.

The Rectangle method draws the bounding rectangle; Ellipse draws an ellipse that touches all sides of the rectangle.

The following method, for example, draws a rectangle filling a form's upper left quadrant, then draws an ellipse in the same area:

procedure TForm1.FormPaint(Sender: TObject);

begin

 Canvas.Rectangle(0, 0, ClientWidth div 2, ClientHeight div 2);

 Canvas.Ellipse(0, 0, ClientWidth div 2, ClientHeight div 2);

end;


Drawing rounded rectangles

Section topics	See also

To draw a rounded rectangle on a canvas,
Call the canvas's RoundRect method.

The first four parameters passed to RoundRect are a bounding rectangle, just as for Rectangle or Ellipse. RoundRect takes two more parameters that indicate how to draw the rounded corners.

The following method, for example, draws a rounded rectangle in a form's upper left quadrant, rounding the corners as sections of a circle with a diameter of 10 pixels:

procedure TForm1.FormPaint(Sender: TObject);

begin

 Canvas.RoundRect(0, 0, ClientWidth div 2, ClientHeight div 2, 10, 10);

end;


Drawing polygons

Section topics	See also

To draw a polygon with any number of sides on a canvas,
Call the Polygon method of the canvas.

Polygon takes an array of points as its only parameter and connects the points with the pen, then connects the last point to the first to close the polygon. After drawing the lines, Polygon uses the brush to fill the area inside the polygon.

For example, the following code draws a right triangle in the lower left half of a form:

procedure TForm1.FormPaint(Sender: TObject);

begin

 Canvas.Polygon([Point(0, 0), Point(0, ClientHeight),

   Point(ClientWidth, ClientHeight)]);

end;


Drawing special parts

Section topics	See also

In addition to all the lines and shapes described, a canvas also provides some more specialized graphics capabilities, such as pie slices, arcs, and chords. You can also flood-fill regions and rectangles and copy images from other canvases. To see details on all the graphics you can draw on a canvas, see TCanvas component.


Techniques for drawing in an application

Section topics	See also

The example code in the rest of these topics is taken from a sample application that ships with Delphi. The application draws lines and shapes on a window's canvas in response to clicks and drags: pressing a mouse button starts drawing, and releasing the button ends the drawing.

To see the sample application and study its code, open the project GRAPHEX.DPR in the DEMOS\DOC\GRAPHEX directory. The following figure shows the finished graphics application, GRAPHEX.EXE:

To start with, the example code shows how to draw on the surface of the main form. Later examples demonstrate drawing on a bitmap.


Responding to the mouse

Section topics	See also

There are four kinds of mouse actions you can respond to in your applications. Three of these are uniquely mouse actions: mouse-button down, mouse moved, and mouse-button up. The fourth kind of action, a click (a complete press-and-release, all in one place) is a bit different, as it can also be generated by some kinds of keystrokes (such as pressing Enter in a modal dialog box).

There are four important topics to understand about mouse events:

What's in a mouse event?

Responding to a mouse-down action

Responding to a mouse-up action

Responding to a mouse move

What's in a mouse event?

There are three mouse events defined in Delphi: OnMouseDown, OnMouseMove, and OnMouseUp.

When a Delphi application detects a mouse action, it calls whatever event handler you've defined for the corresponding event, passing five parameters. Use the information in those parameters to customize your responses to the events. The five parameters are as follows:

Parameter	Meaning
Sender	The object that detected the mouse action
Button	Indicates which mouse button was involved: mbLeft, mbMiddle, or mbRight
Shift	Indicates the state of the Alt, Ctrl, and Shift keys at the time of the mouse action
X, Y	The coordinates where the event occurred

Most of the time, the most important information in a mouse-event handler is the coordinates, but sometimes you also need to check Button to determine which mouse button caused the event.

Note:	Delphi uses the same criteria as Microsoft Windows in determining which mouse button has been pressed. Thus, if you have switched the default "primary" and "secondary" mouse buttons (so that the right mouse button is now the primary button), clicking the primary (right) button will record mbLeft as the value of the Button parameter.

Responding to a mouse-down action

Whenever the user presses a button on the mouse, an OnMouseDown event goes to the object the pointer is over. The object can then respond to the event.

To respond to a mouse-down action,

Attach an event handler to the OnMouseDown event.

Delphi generates an empty handler for a mouse-down event on the form:

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;

 Shift: TShiftState; X, Y: Integer);

begin

end;

Here's code that displays some text at the point where the mouse button is pressed. It uses the X and Y parameters sent to the method, and calls the TextOut method of the canvas to display text there:

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;

 Shift: TShiftState; X, Y: Integer);

begin

 Canvas.TextOut(X, Y, 'Here!');	{ write text at (X, Y) }

end;

When the application runs, you can press the mouse button down with the mouse cursor on the form and have the string appear at the point clicked, as shown in the following figure.

Drawing text at the point clicked

Pressing the button tells the application where the drawing will start, but for a line you need both a starting point and an ending point.

Here's code that sets the current drawing position to the coordinates where the user presses the button:

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;

 Shift: TShiftState; X, Y: Integer);

begin

 Canvas.MoveTo(X, Y);	{ set pen position }

end;

Pressing the mouse button now sets the pen position, setting the line's starting point. To draw a line to the point where the user releases the button, you need to respond to a mouse-up event.

Responding to a mouse-up action

An OnMouseUp event occurs whenever the user releases a mouse button. The event usually goes to the object the mouse cursor is over when the user presses the button, which is not necessarily the same object the cursor is over when the button is released. This enables you, for example, to draw a line as if it extended beyond the border of the form.

To respond to mouse-up actions,

Define a handler for the OnMouseUp event.

Here's a simple OnMouseUp event handler that draws a line to the point of the mouse-button release:

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  Canvas.LineTo(X, Y);	{ draw line from PenPos to (X, Y) }
end;

This code lets a user draw lines by clicking, dragging, and releasing, as shown in the following figure.

Graphics application with lines drawn

In this case, the user can't see the line until the mouse button is released. The following section shows you how to give the user some intermediate feedback by drawing temporary lines while the mouse moves.

Responding to a mouse move

An OnMouseMove event occurs periodically when the user moves the mouse. The event goes to the object that was under the mouse pointer when the user pressed the button.

To respond to mouse movements,

Define an event handler for the OnMouseMove event.

This example, uses mouse-move events to draw intermediate shapes on a form while the user holds down the mouse button, thus providing some feedback to the user. The OnMouseMove event handler draws a line on a form to the location of the OnMouseMove event:

procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  Canvas.LineTo(X, Y);	{ draw line to current position }
end;

With this code, moving the mouse over the form causes drawing to follow the mouse, even before the mouse button is pressed, as shown in the following figure.

Graphics application following the mouse

Mouse-move events occur even when you haven't pressed the mouse button.

If you want to track whether there is a mouse button pressed, you need to add an object field to the form object.


Adding a field to a form object

Section topics	See also

When you add a component to a form, Delphi also adds a field that represents that component to the form object, and you can refer to the component by the name of its field. Component fields are explained in "Programming with Delphi objects." You can also add your own fields to forms by editing the type declaration at the top of the form's unit. In the following example, the form needs to track whether the user has pressed a mouse button. To do that, it adds a Boolean field and sets its value when the user presses the mouse button.

To add a field to an object,
Edit the object's type definition, specifying the field identifier and type after the public directive at the bottom of the declaration.

Delphi "owns" any declarations before the public directive: that's where it puts the fields that represent controls and the methods that respond to events.

The following code gives a form a field called Drawing of type Boolean, in the form object's declaration. It also adds two fields to store points Origin and MovePt of type TPoint.

type
  TForm1 = class(TForm)

   procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);

   procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);

   procedure FormMouseMove(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);

 public
    Drawing: Boolean;	{ field to track whether button was pressed }
    Origin, MovePt: TPoint;	{ fields to store points }
  end;

When you have a Drawing field to track whether to draw, you should set it to True when the user presses the mouse button, and False when the user releases it:

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  Drawing := True;	{ set the Drawing flag }

 Canvas.MoveTo(X, Y);
end;

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  Canvas.LineTo(X, Y);

 Drawing := False;	{ clear the Drawing flag }
end;

Then you can modify the OnMouseMove event handler to draw only when Drawing is True:

procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  if Drawing then	{ only draw if Drawing flag is set }

   Canvas.LineTo(X, Y);
end;

This results in drawing only between the mouse-down and mouse-up events, but you still get a scribbled line that tracks the mouse movements instead of a nice, straight line, as shown:

The problem is that each time you move the mouse, the mouse-move event handler calls LineTo, which moves the pen position, so by the time you release the button, you've lost the point where the straight line was supposed to start.

The next topic shows how to use the Origin and MovePt fields to track the starting and intermediate points.


Refining line drawing

Section topics	See also

With fields in place to track various points, you can refine an application's line drawing.

Tracking the origin point

The most important thing to track when drawing lines is the point where the line starts. As you saw earlier, you know where the line ends, because that's where the mouse-up event occurs. The OnMouseUp event handler needs to know where the line started, too. That's where the Origin field comes in.

Origin must be set to the point where the mouse-down event occurs, so the mouse-up event handler can use Origin to place the beginning of the line, as in this code:

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  Drawing := True;

 Canvas.MoveTo(X, Y);

 Origin := Point(X, Y);	{ record where the line starts }
end;

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  Canvas.MoveTo(Origin.X, Origin.Y);	{ move pen to starting point }

 Canvas.LineTo(X, Y);

 Drawing := False;
end;

Those changes get the application to draw the final line again, but what about the intermediate drawing?

Tracking movement

The problem with the OnMouseMove event handler as currently written is that while it draws a line to the current position of the mouse, it draws that line from the last mouse position, not from the original position.

Now that the application tracks the origin, however, that's easy to correct, by moving the drawing position to the origin point, then drawing to the current point:

procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  if Drawing then
  begin
    Canvas.MoveTo(Origin.X, Origin.Y);	{ move pen to starting point }

   Canvas.LineTo(X, Y);
  end;
end;

That's better, in that you now get lines to the current mouse position. Unfortunately, the lines don't go away, so you can hardly find the final line:

The trick is to erase each line before you draw the next one, and that means you need to keep track of where the previous one was. That's the purpose of the MovePt field added earlier.

MovePt has to be set to the endpoint of each intermediate line, so you can use MovePt and Origin to erase that line the next time a line is drawn:

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  Drawing := True;

 Canvas.MoveTo(X, Y);

 Origin := Point(X, Y);

 MovePt := Point(X, Y);	{ keep track of where this move was }
end;

procedure TForm1.FormMouseMove(Sender: TObject;Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  if Drawing then
  begin
    Canvas.Pen.Mode := pmNotXor;	{ use XOR mode to draw/erase }

   Canvas.MoveTo(Origin.X, Origin.Y);	{ move pen back to origin }

   Canvas.LineTo(MovePt.X, MovePt.Y);	{ erase the old line }

   Canvas.MoveTo(Origin.X, Origin.Y);	{ start at origin again }

   Canvas.LineTo(X, Y);	{ draw the new line }
  end;

 MovePt := Point(X, Y);	{ record point for next move }

 Canvas.Pen.Mode := pmCopy;
end;

Now you get a nice "rubber-band" effect when you draw the line. By changing the pen's mode to pmNotXor, you have it combine your line with the background pixels. When you go to erase the line, you're actually setting the pixels back to the way they were. By changing the pen mode back to pmCopy (its default value) after drawing the lines, you ensure that the pen is ready to do its final drawing when you release the mouse button:

This information is useful for drawing different shapes, such as rectangles and ellipses, as well as lines.


Drawing with different tools

Section topics	See also

These topics show how to respond to clicks on speed buttons by setting the drawing tool for a form. They include

Declaring an enumerated type

Changing the tool with speed buttons

Using drawing tools


Declaring an enumerated type

Section topics	See also

A graphics program needs a way to keep track of what kind of drawing tool you want it to use at any given time. You could assign numbers to each kind of tool, but then you would have to remember what each number stands for. You can do that more easily by assigning mnemonic constant names to each number, but your code won't be able to distinguish which numbers are in the proper range and of the right type. Fortunately, Object Pascal provides a means to handle both of these shortcomings. You can declare an enumerated type.

An enumerated type is really just a shorthand way of assigning sequential values to constants. Since it's also a type declaration, you can use Object Pascal's type-checking to ensure that you assign only those specific values.

To declare an enumerated type,
Use the reserved work type, followed by an identifier for the type, then an equal sign, and the identifiers for the values in the type in parentheses, separated by commas.

For example, the following code declares an enumerated type for each of the drawing tools in a graphics application:

type
  TDrawingTool = (dtLine, dtRectangle, dtEllipse, dtRoundRect);

By convention, type identifiers begin with the letter T, and groups of similar constants (such as those making up an enumerated type) begin with a 2-letter prefix (such as dt for "drawing tool").

The declaration of the TDrawingTool type is equivalent to declaring a group of constants:

const
  dtLine = 0;

 dtRectangle = 1;

 dtEllipse = 2;
  dtRoundRect = 3;

The main difference is that by declaring the enumerated type, you give the constants not just a value, but also a type, which enables you to use Object Pascal's type-checking to prevent many errors. A variable of type TDrawingTool can be assigned only one of the constants dtLine..dtRoundRect. Attempting to assign some other number (even one in the range 0..3) generates a compile-time error.

In the following code, a field added to a form keeps track of the form's drawing tool:

type
  TDrawingTool = (dtLine, dtRectangle, dtEllipse, dtRoundRect);

 TForm1 = class(TForm)
    ...	{ method declarations }

 public
    Drawing: Boolean;

   Origin, MovePt: TPoint;

   DrawingTool: TDrawingTool;	{ field to hold current tool }
  end;

Since objects (including forms) initialize all their fields to zero, the drawing tool when you start the application is dtLine.


Changing the tool with speed buttons

Section topics	See also

Each drawing tool in your application needs an OnClick event handler. Suppose your application had a tool bar button for each of four drawing tools: line, rectangle, ellipse, and rounded rectangle. You would attach the following event handlers to the OnClick events of the four drawing-tool buttons, setting DrawingTool to the appropriate value for each:

procedure TForm1.LineButtonClick(Sender: TObject);	{ LineButton }
begin
  DrawingTool := dtLine;
end;

procedure TForm1.RectangleButtonClick(Sender: TObject);	{ RectangleButton }
begin
  DrawingTool := dtRectangle;
end;

procedure TForm1.EllipseButtonClick(Sender: TObject);	{ EllipseButton }
begin
  DrawingTool := dtEllipse;
end;

procedure TForm1.RoundedRectButtonClick(Sender: TObject);	{ RoundRectButton }
begin
  DrawingTool := dtRoundRect;
end;

Now that you can tell the form what kind of tool to use, you need to tell it how to draw the different shapes. The only methods that do any drawing are the mouse-move and mouse-up handlers, and the only drawing code draws lines, no matter what tool is selected.


Using drawing tools

Section topics	See also

To use different drawing tools, your code needs to specify how to draw, based on the selected tool. The instruction is added to each tool's event handler.

These topics describe:

Making efficient choices

Drawing the appropriate shapes

Adding a method to a form


Making efficient choices

Section topics	See also

If you had only a few choices, you would probably just rewrite the drawing methods to look something like this:

procedure TForm1.FormMouseUp(Sender: TObject);
begin
  if DrawingTool = dtLine then { draw a line }

 else if DrawingTool = dtRectangle then { draw a rectangle }

 { ... and so on ... }
end;

The comments like { draw a line }, of course, represent blocks of statements such as those shown in earlier code examples.

Once you get more than a few choices, though, this becomes tedious and inefficient. Fortunately, Object Pascal and Delphi offer an easier way to switch among alternatives, the case statement. Among other things, case statements make it easier to add new choices later.

The method shown above could be rewritten using a case statement instead of the if..then..else statements:

procedure TForm1.FormMouseUp(Sender: TObject);
begin
  case DrawingTool of
    dtLine: { draw a line }

   dtRectangle: { draw a rectangle }
    { ... and so on ... }

 end;
end;

Drawing the appropriate shapes

Drawing shapes is just as easy as drawing lines: Each one takes a single statement; you just need the coordinates.

Here's a rewrite of the OnMouseUp event handler that draws shapes for all four tools:

procedure TForm1.FormMouseUp(Sender: TObject);
begin
  case DrawingTool of
    dtLine:
      begin
        Canvas.MoveTo(Origin.X, Origin.Y);

       Canvas.LineTo(X, Y)
      end;

   dtRectangle: Canvas.Rectangle(Origin.X, Origin.Y, X, Y);

   dtEllipse: Canvas.Ellipse(Origin.X, Origin.Y, X, Y);

   dtRoundRect: Canvas.RoundRect(Origin.X, Origin.Y, X, Y,
                   (Origin.X - X) div 2, (Origin.Y - Y) div 2);
  end;

 Drawing := False;
end;

Of course, you also need to update the OnMouseMove handler to draw shapes:

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  if Drawing then
  begin
    Canvas.Pen.Mode := pmNotXor;

   case DrawingTool of
      dtLine: begin
                Canvas.MoveTo(Origin.X, Origin.Y);

               Canvas.LineTo(MovePt.X, MovePt.Y);

               Canvas.MoveTo(Origin.X, Origin.Y);

               Canvas.LineTo(X, Y);
              end;

     dtRectangle: begin
                      Canvas.Rectangle(Origin.X, Origin.Y, MovePt.X, MovePt.Y);

                     Canvas.Rectangle(Origin.X, Origin.Y, X, Y);
                   end;

     dtEllipse: begin
                    Canvas.Ellipse(Origin.X, Origin.Y, X, Y);

                   Canvas.Ellipse(Origin.X, Origin.Y, X, Y);
                  end;

     dtRoundRect: begin
                     Canvas.RoundRect(Origin.X, Origin.Y, X, Y,
                       (Origin.X - X) div 2, (Origin.Y - Y) div 2);

                    Canvas.RoundRect(Origin.X, Origin.Y, X, Y,
                       (Origin.X - X) div 2, (Origin.Y - Y) div 2);
                   end;
    end;

   MovePt := Point(X, Y);
  end;

 Canvas.Pen.Mode := pmCopy;
end;

You probably notice that there is a lot of repeated code in those methods. In fact, the code to draw each shape appears three different times. This is a good example of a time when you should move repeated code into a separate routine. The following section shows all the shape-drawing code in a single method that both mouse-event handlers can call.

Adding a method to a form

Any time you find that a number of your event handlers use the same code, you can make your application more efficient by moving the repeated code into a method that all the event handlers can share.

To add a method to a form:

1.	Add the method declaration to the form object.

You can add the declaration in either the public or private parts at the end of the form object's declaration. If the code is just sharing the details of handling some events, it's probably safest to make the shared method private.

2.	Write the method implementation in the implementation part of the form unit.

The header for the method implementation must match the declaration exactly, with the same parameters in the same order.

The following code adds a method to the form called DrawShape and calls it from each of the handlers. First, the declaration of DrawShape is added to the form object's declaration:

type
  TForm1 = class(TForm)
    ...	{ many fields and methods omitted for brevity }

 public
    { Public declarations }

   procedure DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);
  end;

Then, the implementation of DrawShape is written in the implementation part of the unit:

implementation
{$R *.FRM}

...	{ many other method implementations omitted for brevity }

procedure TForm1.DrawShape(TopLeft, BottomRight: TPoint; AMode: TPenMode);
begin
  with Canvas do
  begin
    Pen.Mode := AMode;

   case DrawingTool of
      dtLine:
        begin
          MoveTo(TopLeft.X, TopLeft.Y);

         LineTo(BottomRight.X, BottomRight.Y);
        end;

     dtRectangle: Rectangle(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y);

     dtEllipse: Ellipse(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y);

     dtRoundRect: RoundRect(TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y,
        (TopLeft.X - BottomRight.X) div 2, (TopLeft.Y - BottomRight.Y) div 2);
    end;
  end;
end;

The other event handlers are modified to call DrawShape.

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  DrawShape(Origin, Point(X, Y), pmCopy);	{ draw the final shape }

 Drawing := False;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);

begin
  if Drawing then
  begin
    DrawShape(Origin, MovePt, pmNotXor);	{ erase the previous shape }

   MovePt := Point(X, Y);	{ record the current point }

   DrawShape(Origin, MovePt, pmNotXor);	{ draw the current shape }
  end;
end;


Customizing pens and brushes

Section topics	See also

To add color to drawings, you need to modify the pen and brush the canvas uses to do its drawing and painting.

First, you need to add tool bars to the application, one each for the pen and brush. Then, these topics show how to perform the following tasks:

Changing the pen style

Changing the pen color

Changing the pen width

Changing the brush style

Changing the brush color


Changing the pen style

Section topics	See also

A pen's style determines what kind of lines the pen draws, such as solid, dashed, or dotted. Note that some video drivers do not support different styles for pens with a pixel width greater than one. Those drivers generally make all larger pens solid, no matter what style you specify.

To change the style of a pen,

Set the pen's Style property to the value corresponding to the effect you want: psSolid, psDash, psDot, psDashDot, psDashDotDot, or psClear.

You could add click-event handlers for each of the pen-style buttons on the pen's tool bar, but sharing one event handler for all the pen-style buttons is more efficient.


Sharing an event handler

Section topics	See also

Different controls can all use the same event handler to handle events, as long as the events are compatible, meaning their handlers take the same parameters in the same order. The most common use of this is to provide different entry points to do the same action, such as attaching the click events for a menu item and a button to the same handler, which gives a user multiple ways to perform the same action. But you can also have several different controls share a handler that does different things depending on which control called it.

To share an event handler,

Assign the same event handler to the events of multiple controls. If you need to determine which control actually got the event, check the Sender parameter.

Suppose you wanted to create one click-event handler for six pen-style buttons on a pen's tool bar, attach the handler to each of the controls, then use Sender to determine which button to respond to. Here's what you would do:

1.	Select all six pen-style buttons.

2.	On the Events page in the Object Inspector, select OnClick.

3.	In the Handler column, type SetPenStyle and press Enter. Delphi generates an empty click-event handler called SetPenStyle and attaches it to the OnClick events of all six buttons.

4.	Fill in the click-event handler by setting the pen's style depending on the value of Sender, which is the control that sent the click event:

procedure TForm1.SetPenStyle(Sender: TObject);
begin
  with Canvas.Pen do
  begin
    if Sender = SolidPen then Style := psSolid

   else if Sender = DashPen then Style := psDash

   else if Sender = DotPen then Style := psDot

   else if Sender = DashDotPen then Style := psDashDot

   else if Sender = DashDotDotPen then Style := psDashDotDot

   else if Sender = ClearPen then Style := psClear;
  end;
end;


Changing the pen color

Section topics	See also

A pen's color determines the color of the lines the pen draws, including lines drawn as the boundaries of shapes, as well as other lines and polylines.

To change the pen color,
Assign a value to the Color property of the pen.

To let the user choose a new color for the pen, you need to put a color grid on the pen's tool bar. A color grid can set both foreground and background colors (for example, it's the same control used to pick the syntax-highlight colors in Delphi). For a pen, you need only the foreground color.

Since the user chooses a new color by clicking the grid, this code changes the pen's color in response to the OnClick event:

procedure TForm1.PenColorClick(Sender: TObject);
begin
  Canvas.Pen.Color := PenColor.ForegroundColor;
end;


Changing the pen width

Section topics	See also

A pen's width determines the thickness, in pixels, of the lines it draws. Remember that when the thickness is greater than 1, some video drivers always draw solid lines, no matter what the value of the pen's Style property.

To change the pen width,

Assign a numeric value to the pen's Width property.

Suppose you had a scroll bar on the pen's tool bar to set width values for the pen. And suppose you wanted to update the label next to the scroll bar to provide feedback to the user. Using the scroll bar's position to determine the pen width, you need to update the pen width every time the position changes.

This is how you would handle the scroll bar's OnChange event:

procedure TForm1.PenWidthChange(Sender: TObject);
begin
  Canvas.Pen.Width := PenWidth.Position;	{ set the pen width directly }

 PenSize.Caption := IntToStr(PenWidth.Position);	{ convert to string for caption }
end;


Changing the brush style

Section topics	See also

A brush style determines what pattern the canvas uses to fill shapes. The predefined styles include solid color, no color, and various line and hatch patterns.

To change the style of a brush,

Set its Style property to one of the predefined values: bsSolid, bsClear, bsHorizontal, bsVertical, bsFDiagonal, bsBDiagonal, bsCross, or bsDiagCross.

This example sets brush styles by sharing a click-event handler for all of a set of eight brush-style buttons. All eight buttons are selected, then OnClick is selected on the Events page of the Object Explorer, and the OnClick handler is named SetBrushStyle. Here is the handler code:

procedure TForm1.SetBrushStyle(Sender: TObject);
begin
  with Canvas.Brush do
  begin
    if Sender = SolidBrush then Style := bsSolid

   else if Sender = ClearBrush then Style := bsClear

   else if Sender = HorizontalBrush then Style := bsHorizontal

   else if Sender = VerticalBrush then Style := bsVertical

   else if Sender = FDiagonalBrush then Style := bsFDiagonal

   else if Sender = BDiagonalBrush then Style := bsBDiagonal

   else if Sender = CrossBrush then Style := bsCross

   else if Sender = DiagCrossBrush then Style := bsDiagCross;
  end;
end;


Changing the brush color

Section topics	See also

A brush's color determines what color the canvas uses to fill shapes.

To change the fill color,

Assign a value to the brush's Color property.

You can set the brush color just as you do the pen color, in response to a click on a color grid on the brush's tool bar (see "Changing the pen color"):

procedure TForm1.BrushColorClick(Sender: TObject);
begin
  Canvas.Brush.Color := BrushColor.ForegroundColor;
end;


Drawing on a bitmap

Section topics	See also

The times when you want to draw directly on a form are relatively rare. More often, an application should draw on a bitmap, since bitmaps are very flexible for operations such as copying, printing, and saving. Delphi's image control is a component that can contain a bitmap, making it easy to put one or more bitmaps into a form.

In addition, the bitmap need not be the same size as the form: it can be either smaller or larger. By adding a scroll box control to the form and placing the image inside it, you can draw on bitmaps that are much larger than the form or even larger than the screen.

Adding a scrollable bitmap for drawing takes two steps:

Adding a scrollable region

Adding an image control

Once you move the application's drawing to the bitmap in the image control, it is easy to add printing, Clipboard, and loading and saving operations for bitmap files.


Adding an image control

Section topics	See also

An image control is a kind of placeholder component. It allows you to specify an area on a form that will contain a picture object, such as a bitmap or a metafile. You can either set the size of the image manually or allow the image control to adjust to the size of its picture at runtime.

You can use an image control to hold a bitmap that isn't necessarily displayed all the time, or which an application needs to use to generate other pictures. "Creating an owner-draw control" shows how to use image controls to hold bitmaps used to generate the items in an owner-draw control.

Placing the control

You can place an image control anywhere on a form. If you take advantage of the image control's ability to size itself to its picture, your only concern is setting the top left corner. If the image control is a nonvisible holder for a bitmap, you can place it anywhere, just as you would a nonvisual component.

If you drop the image control on a scroll box already aligned to the form's client area, this assures that the scroll box will add any scroll bars necessary to access offscreen portions of the image's picture. Then set the image control's properties.

Setting the initial bitmap size

When you place an image control, it has no picture. You've created the placeholder, but haven't given it anything to display. If the control will always hold a particular picture, you can set the image control's Picture property at design time. The control can also load its picture from a file at runtime, as described in "Working with graphics files." If you just need a blank bitmap for drawing, however, you should create it at runtime.

To create a blank bitmap when the application starts:

1.	Attach a handler to the OnCreate event for the form that contains the image.

2.	Create a bitmap object, and assign it to the image control's Picture.Graphic property.

In this example, the image is in the application's main form, Form1, so the code attaches a handler to Form1's OnCreate event:

procedure TForm1.FormCreate(Sender: TObject);
var
  Bitmap: TBitmap;	{ temporary variable to hold the bitmap }

begin
  Bitmap := TBitmap.Create;	{ construct the bitmap object }

 Bitmap.Width := 200;	{ assign the initial width... }

 Bitmap.Height := 200;	{ ...and the initial height }

 Image.Picture.Graphic := Bitmap;	{ assign the bitmap to the image control }
end;

Assigning the bitmap to the picture's Graphic property gives ownership of the bitmap to the picture object. It will therefore destroy the bitmap when it finishes with it, so you should not destroy the bitmap object. You can assign a different bitmap to the picture (see "Replacing the picture"), at which point the picture disposes of the old bitmap and assumes control of the new one.

If you run the application now, you see that client area of the form has a white region, representing the bitmap. If you size the window so that the client area cannot display the entire image, you'll see that the scroll box automatically shows scroll bars to allow display of the rest of the image. But if you try to draw on the image, you don't get any graphics, because the application is still drawing on the form, which is now behind the image and the scroll box.

Drawing on the bitmap

To draw on a bitmap:

1.	Use the image control's canvas.

2.	Attach the mouse-event handlers to the appropriate events in the image control.

Examples of such handlers are given in earlier topics.


See also

TCanvas

Working with graphics files


Creating an owner-draw control

Section topics	See also

Windows list-box and combo-box controls have a style available called "owner draw," which means that instead of using Windows' standard method of drawing text for each item in the control, the control's owner (generally, the form) draws each item at runtime. The most common application for owner-draw controls is to provide graphics instead of, or in addition to, text for items. The Delphi tab-set control also has a similar owner-draw style.

Owner-draw controls have one thing in common: they all contain lists of items. By default, those lists are lists of strings, which Windows displays as text. Delphi enables you to associate an object with each item in a list and gives you the chance to use that object when drawing items. String lists are explained in more detail in "Working with string lists."

In general, creating an owner-draw control in Delphi takes three steps:

1.	Setting the owner-draw style

2.	Adding graphical objects to a string list

3.	Drawing owner-draw items


Setting the owner-draw style

Section topics	See also

Each control that has an owner-draw variant has a property called Style. Style determines whether the control uses the default drawing (called the "standard" style) or owner drawing.

For list boxes and combo boxes, there is also a choice of owner-draw styles, called fixed and variable, as the following table describes. Owner-draw tab sets are always variable. Although the size of each row and column in an owner-draw grid might vary, the size of each cell is fixed before drawing the grid.

Owner-draw style	Meaning	Examples
Fixed	Each item is the same height, with that height determined by the ItemHeight property.	lbOwnerDrawFixed, csOwnerDrawFixed
Variable	Each item might have a different height, determined by the data at runtime.	lbOwnerDrawVariable, csOwnerDrawVariable, tsOwnerDraw


Adding graphical objects to a string list

Section topics	See also

Every Delphi string list has the ability to hold a list of objects in addition to its list of strings. That is, in addition to its indexed Strings property, which contains strings, a string list also has an Objects property. explains more about using objects with string lists.


Adding images to an application

Section topics	See also

An image control is a nonvisual control that contains a graphical image, such as a bitmap. You use image controls to display graphical images on a form, but you can also use them to hold hidden images that you'll use in your application. For example, you can store bitmaps for owner-draw controls in hidden image controls, like this:


Adding images to a string list

Section topics	See also

Once you have graphical images in an application, you can associate them with the strings in a string list. You can either add the objects at the same time as the strings, or associate objects with already-added strings. If you have all the needed data available, you should generally add strings and objects together.


Drawing owner-draw items

Section topics	See also

When you set a control's style to owner draw, Windows no longer draws the control on the screen. Instead, it generates events for each visible item in the control. Your application handles the events to draw the items.

The following steps are repeated for each visible item in the control, but you use a single event handler for all items.

To draw the items in an owner-draw control:

1.	Size the item.

If the owner-draw items are all the same size (for example, with a list box style of lsOwnerDrawFixed), you don't need to do this step.

2.	Draw the item.


Sizing owner-draw items

Section topics	See also

Before giving your application the chance to draw each item in a variable owner-draw control, Windows generates a measure-item event. The measure-item event tells the application where the item will appear on the control.

Windows determines what size the item will probably be (generally just large enough to display the item's text in the current font). Your application can handle the event and change the rectangle Windows chose. For example, if you plan to substitute a bitmap for the item's text, you'd change the rectangle to be the size of the bitmap. If you want the bitmap and text, you adjust the rectangle to be big enough for both.

To change the size of an owner-draw item,
Attach an event handler to the measure-item event in the owner-draw control.

Depending on the control, the name of the event will vary. List boxes and combo boxes use OnMeasureItem. Tab sets use OnMeasureTab. Grids have no measure-item event.

The sizing event has two important parameters: the index number of the item and the size of that item. That size is variable: the application can make it either smaller or larger. The positions of subsequent items depend on the size of preceding items.

For example, in a variable owner-draw list box, if the application sets the height of the first item to five pixels, the second item starts at the sixth pixel down from the top, and so on. In list boxes and combo boxes, the only aspect of the item the application can alter is height of the item. The width of the item is always the width of the control. In tab sets, only the width of the tabs varies, since the size of the control fixes the height of the tabs.

Note:	Owner-draw grids cannot change the sizes of their cells as they draw. The size of each row and column is set before drawing by the ColWidths and RowHeights properties.

Example

The following code adjusts the width of an owner-draw tab set's tabs to handle both the default text and a bitmap.

For a tab set, the default width of each tab is the width of the text it contains. To accommodate bitmaps next to the text, you need to handle the measure-item event, increasing the tab width value to include enough space for both the graphic and the text.

The following code, attached to the OnMeasureItem event of an owner-draw tab set, increases the width of each tab to accommodate its associated bitmap:

procedure TFMForm.DriveTabSetMeasureTab(Sender: TObject; Index: Integer;
  var TabWidth: Integer);	{ note that TabWidth is a var parameter}

var
  BitmapWidth: Integer;

begin
  BitmapWidth := TBitmap(DriveTabSet.Tabs.Objects[Index]).Width;

 { increase tab width by the width of the associated bitmap plus two }

 Inc(TabWidth, 2 + BitmapWidth);
end;

Note:	You must typecast the items from the Objects property in the string list. Objects is a property of type TObject so that it can hold any kind of object. When you retrieve objects from the array, you need to typecast them back to the actual type of the items.


Drawing each owner-draw item

Section topics	See also

When an application needs to draw or redraw an owner-draw control, Windows generates draw-item events for each visible item in the control.

To draw each item in an owner-draw control,
Attach an event handler to the draw-item event for that control.

The names of events for owner drawing always start with OnDraw, such as OnDrawItem, OnDrawTab, or OnDrawCell.

The draw-item event contains parameters indicating the index of the item to draw, the rectangle in which to draw, and usually some information about the state of the item (such as whether the item has focus). The application handles each event by rendering the appropriate item in the given rectangle.

Example

For example, the following code shows how to draw tabs in a tab set that has bitmaps associated with each string. It attaches this handler to the OnDrawTab event for the tab set:

procedure TFMForm.DriveTabSetDrawTab(Sender: TObject; TabCanvas: TCanvas;
  R: TRect; Index: Integer; Selected: Boolean);

var
  Bitmap: TBitmap;

begin
  Bitmap := TBitmap(DriveTabSet.Tabs.Objects[Index]);

 with TabCanvas do
  begin
    Draw(R.Left, R.Top + 4, Bitmap);	{ draw bitmap }

   TextOut(R.Left + 2 + Bitmap.Width,	{ position text }
      R.Top + 2, DriveTabSet.Tabs[Index]);	{ and draw it to the right of the bitmap }
  end;
end;

Note:	Most draw-item events don't pass a canvas object as a parameter; you normally use the canvas of the control being drawn. Because the tab set also has to render its tab separators between items, it passes a special canvas for item drawing as a parameter.


See also

TComboBox

TDBComboBox

TListBox

TDBListBox

TDBLookupComboBox

TDBLookupListBox


Working with graphics files

Section topics	See also

Graphic images that exist only for the duration of one running of an application are of very limited value. Often, you either want to use the same picture every time, or you want to save a created picture for later use. Delphi's image control makes it easy to load pictures from a file and save them again.

The logic for loading and saving graphics files is the same as for any other files. The purpose of these topics is to introduce the file-loading and file-saving capabilities of the graphics objects, including

Loading a picture from a file

Saving a picture to a file

Replacing the picture

These topics also discuss using the Clipboard with graphics.


Loading a picture from a file

Section topics	See also

The ability to load a picture from a file is important if your application needs to modify the picture or if you want to store the picture outside the application so a person or another application can change the picture without changing code.

To load a graphics file into an image control,
 Call the LoadFromFile method of the image control's Picture object.

The following code gets a file name from an open-file dialog box, and then loads that file into an image control named Image:

procedure TForm1.Open1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then
  begin
    CurrentFile := OpenDialog1.FileName;

   Image.Picture.LoadFromFile(CurrentFile);
  end;
end;


Saving a picture to a file

Section topics	See also

When you have created or modified a picture, you often want to save the picture in a file for later use. The Delphi picture object can save graphics in several formats, and application developers can create and register their own graphic-file formats so that picture objects can store them as well.

To save the contents of an image control in a file,
 Call the SaveToFile method of the image control's Picture object.

The SaveToFile method requires the name of a file to save into. If the picture is newly created, it might not have a file name, or a user might want to save an existing picture in a different file. In either case, the application needs to get a file name from the user before saving, as shown in the next topic.

The following pair of event handlers, attached to the File|Save and File|Save As menu items, respectively, handle the resaving of named files, saving of unnamed files, and saving existing files under new names.

procedure TForm1.Save1Click(Sender: TObject);
begin
  if CurrentFile <> '' then
    Image.Picture.SaveToFile(CurrentFile)	{ save if already named }

 else SaveAs1Click(Sender);	{ otherwise get a name }
end;

procedure TForm1.Saveas1Click(Sender: TObject);
begin
  if SaveDialog1.Execute then	{ get a file name }
  begin
    CurrentFile := SaveDialog1.FileName;	{ save the user-specified name }

   Save1Click(Sender);	{ then save normally }
  end;
end;


Replacing the picture

Section topics	See also

You can replace the picture in an image control at any time. If you assign a new graphic to a picture that already has a graphic, the new graphic replaces the existing one.

To replace the picture in an image control,
a new graphic to the image control's Picture object.

Creating the new graphic is the same process you used to create the initial graphic (see "Setting the initial bitmap size"), but you should also provide a way for the user to choose a size other than the default size used for the initial graphic. An easy way to provide that option is to present a dialog box, such as the one in:

With such a dialog box in your project, add it to the uses clause in the unit for the main form. You can then attach an event handler to the File|New menu item's OnClick event. Here's an example:

procedure TForm1.New1Click(Sender: TObject);
var
  Bitmap: TBitmap;	{ temporary variable for the new bitmap }

begin
  with NewBMPForm do
  begin
    ActiveControl := WidthEdit;	{ make sure focus is on width field }

   WidthEdit.Text := IntToStr(Image.Picture.Graphic.Width);	{ use current dimensions... }

   HeightEdit.Text := IntToStr(Image.Picture.Graphic.Height);	{ ...as default }

   if ShowModal <> idCancel then	{ continue if user doesn't cancel dialog box }
    begin
      Bitmap := TBitmap.Create;	{ create fresh bitmap object }

     Bitmap.Width := StrToInt(WidthEdit.Text);	{ use specified width }

     Bitmap.Height := StrToInt(HeightEdit.Text);	{ use specified height }

     Image.Picture.Graphic := Bitmap;	{ replace graphic with new bitmap }

     CurrentFile := '';	{ indicate unnamed file }
    end;
  end;
end;

The important aspect to understand is that assigning a new bitmap to the picture object's Graphic property causes the picture object to destroy the existing bitmap and take ownership of the new one. Delphi handles the details of freeing the resources associated with the previous bitmap automatically.


Using the Clipboard with graphics

Section topics	See also

You can use the Windows Clipboard to copy and paste graphics within your applications or to exchange graphics with other applications. Delphi's Clipboard object makes it easy to handle different kinds of information, including graphics.

Before you can use the Clipboard object in your application, you must add the Clipbrd unit to the uses clause of any unit that needs to access Clipboard data.


Copying graphics to the Clipboard

Section topics	See also

You can copy any picture, including the contents of image controls, to the Clipboard. Once on the Clipboard, the picture is available to all Windows applications.

To copy a picture to the Clipboard,
Assign the picture to the Clipboard object using the Assign method.This code shows how to copy the picture from an image control named Image to the Clipboard in response to a click on an Edit|Copy menu item:

procedure TForm1.Copy1Click(Sender: TObject);
begin
  Clipboard.Assign(Image.Picture);
end;


Cutting graphics to the Clipboard

Section topics	See also

Cutting a graphic to the Clipboard is exactly like copying it, but you also erase the graphic from the source.

To cut a graphic from a picture to the Clipboard,
Copy it to the Clipboard, then erase the original.

In most cases, the only issue with cutting is how to effect the erasure of the original image. Setting the area to white is a common solution, as shown in the following code that attaches an event handler to the OnClick event of the Edit|Cut menu item:

procedure TForm1.Cut1Click(Sender: TObject);
var
  ARect: TRect;

begin
  Copy1Click(Sender);	{ copy picture to Clipboard }

 with Image.Canvas do
  begin
    CopyMode := cmWhiteness;	{ copy everything as white }

   ARect := Rect(0, 0, Image.Width, Image.Height);	{ get bitmap rectangle }

   CopyRect(ARect, Image.Canvas, ARect);	{ copy bitmap over itself }

   CopyMode := cmSrcCopy;	{ restore normal mode }
  end;
end;


Pasting graphics from the Clipboard

Section topics	See also

If the Windows Clipboard contains a bitmapped graphic, you can paste it into any image object, including image controls and the surface of a form.

To paste a graphic from the Clipboard:

1.	Call the Clipboard's HasFormat method to see whether the Clipboard contains a graphic.

HasFormat is a Boolean function. It returns True if the Clipboard contains an item of the type specified in the parameter. To test for graphics, you pass CF_BITMAP.

2.	Assign the Clipboard to the destination.

This code shows how to paste a picture from the Clipboard into an image control in response to a click on an Edit|Paste menu item:

procedure TForm1.PasteButtonClick(Sender: TObject);
var
  Bitmap: TBitmap;

begin

 if Clipboard.HasFormat(CF_BITMAP) then 	{ is there a bitmap on the Clipboard? )

 begin

   Bitmap := TBitmap.Create;	{ create bitmap to hold the contents on the Clipboard }

   try

     Bitmap.Assign(Clipboard);	{ get the bitmap off the Clipboard }

     Image.Canvas.Draw(0, 0, Bitmap);	{ copy the bitmap to the Image }

   finally

     Bitmap.Free;

   end;

 end;

end;


See also

Printing graphics

Selecting files to open

TOpenPictureDialog

Saving files

TSavePictureDialog

TClipboard


Manipulating files

Section topics	See also

There are several common file operations built into Object Pascal's runtime library. The procedures and functions for working with files operate at a high level: you specify the name of the file you want to work on, and the routine makes the necessary calls to the operating system for you.

Previous versions of the Pascal language perform similar operations on files themselves, rather than on file names. That is, you have to locate a file and assign it to a file variable before you can, for example, rename the file. By operating at the higher level, Object Pascal reduces your coding burden and streamlines your applications. The lower-level functions are still available, but you shouldn't need them as often.

These are the file manipulations that Object Pascal handles for your applications:

Deleting a file

Changing a file's attributes

Moving, copying, and renaming files

Executing an application


Deleting a file

Section topics	See also

Deleting a file erases the file from the disk and removes the entry from the disk's directory. There is no corresponding operation to restore a deleted file, so applications should generally allow users to confirm deletions of files.

To delete a file,
 Pass the name of the file to the DeleteFile function.

DeleteFile returns True if it deleted the file and False if it did not (for example, if the file did not exist or if it was read-only).

Example

The following code handles a click on a File|Delete menu item by deleting the selected file in a file list box, then updating the list so it reflects the deletion.

procedure TFMForm.Delete1Click(Sender: TObject);
begin
  with FileList do
    if DeleteFile(FileName) then Update;
end;


Changing a file's attributes

Section topics	See also

Every file has various attributes stored by the operating system as bitmapped flags. File attributes include such items as whether a file is read-only or a hidden file.

Changing a file's attributes requires three steps:

1.	Reading file attributes

2.	Changing individual file attributes

3.	Setting file attributes

You can use the reading and setting operations independently, if you only want to determine a file's attributes, or if you want to set an attribute regardless of previous settings. To change attributes based on their previous settings, however, you need to read the existing attributes, modify them, and write the modified attributes.


Reading file attributes

Section topics	See also

Operating systems store file attributes in various ways, generally as bitmapped flags.

To read a file's attributes,
 Pass the file name to the FileGetAttr function.

The return value is a group of bitmapped file attributes of type Word.


Changing individual file attributes

Section topics	See also

Because Delphi represents file attributes in a set, you can use normal bitwise operators to manipulate the individual attributes. Each attribute has a mnemonic name defined in the SysUtils unit.

For example, to set a file's read-only attribute, you would do the following:

Attributes := Attributes or faReadOnly;

You can also set or clear several attributes at once. For example, to clear both the system-file and hidden attributes, do the following:

Attributes := Attributes and not (faSysFile or faHidden);


Setting file attributes

Section topics	See also

Delphi enables you to set the attributes for any file at any time.

To set a file's attributes,
Pass the name of the file and the attributes you want to the FileSetAttr procedure.

Example

The following code reads a file's attributes into a set variable, sets the check boxes in a file-attribute dialog box to represent the current attributes, then executes the dialog box. If the user changes and accepts any dialog box settings, the code sets the file attributes to match the changed settings:

procedure TFMForm.Properties1Click(Sender: TObject);
var
  Attributes, NewAttributes: Word;

begin
  with FileAttrForm do	{attribute form previously created}
  begin
    FileDirName.Caption := FileList.Items[FileList.ItemIndex];	{ set box caption }

   FilePathName.Caption := FileList.Directory;	{ show directory name }

   ChangeDate.Caption := DateTimeToStr(FileDateToDateTime(FileAge(FileList.FileName)));

   Attributes := FileGetAttr(FileDirName.Caption);	{ read file attributes }

   ReadOnly.Checked := (Attributes and faReadOnly) = faReadOnly;

   Archive.Checked := (Attributes and faArchive) = faArchive;

   System.Checked := (Attributes and faSysFile) = faSysFile;

   Hidden.Checked := (Attributes and faHidden) = faHidden;

   if ShowModal <> mrCancel then	{ execute dialog box }
    begin
      NewAttributes := Attributes;	{ start with original attributes }

     if ReadOnly.Checked then NewAttributes := NewAttributes or faReadOnly
      else NewAttributes := NewAttributes and not faReadOnly;

     if Archive.Checked then NewAttributes := NewAttributes or faArchive
      else NewAttributes := NewAttributes and not faArchive;

     if System.Checked then NewAttributes := NewAttributes or faSysFile
      else NewAttributes := NewAttributes and not faSysFile;

     if Hidden.Checked then NewAttributes := NewAttributes or faHidden
      else NewAttributes := NewAttributes and not faHidden;

     if NewAttributes <> Attributes then	{ if anything changed... }
        FileSetAttr(FileDirName.Caption, NewAttributes);	{ ...write the new values }
    end;
  end;
end;


Moving, copying, and renaming files

Section topics	See also

Moving, copying, and renaming files are all similar operations, in that they all produce a file from another file. The difference is in how they treat the original file. Copying leaves the original file alone. Renaming changes the name of the original file. Moving deletes the original file after copying it to its new location.

The runtime library provides a function called RenameFile that handles renaming. The FMXUtils unit in the directory with the file-manager example provides similar procedures called MoveFile and CopyFile for those operations. Each routine takes two string parameters: the name of the original file and the name of the destination file. In fact, you can easily provide a single method in your application that handles any of the three operations.

The following code shows a method called ConfirmChange that displays a confirmation dialog box, then performs the specified operation if confirmed:

procedure TFMForm.ConfirmChange(const ACaption, FromFile, ToFile: string);
begin
  if MessageDlg(Format('%s %s to %s?', [ACaption, FromFile, ToFile]),
    mtConfirmation, [mbYes, mbNo], 0) = mrYes then

 begin
    if ACaption = 'Move' then
      MoveFile(FromFile, ToFile)

   else if ACaption = 'Copy' then
      CopyFile(FromFile, ToFile)

   else if ACaption = 'Rename' then
      RenameFile(FromFile, ToFile);

   FileList.Update;
  end;
end;


Executing an application

Section topics	See also

Many times an application needs to execute another application, either to perform a specific task or just to have that application run concurrently. The Windows API provides a function, ShellExecute, that executes an application, but it requires a number of parameters superfluous to Delphi applications. The FMXUtils unit provides a more useful alternative, called ExecuteFile.

ExecuteFile operates in two different ways. If passed the name of an executable file, ExecuteFile runs that application. If passed the name of a document with an associated application, ExecuteFile runs the application, automatically opening that document at startup.


Executing a file from a file list box

Section topics	See also

ExecuteFile takes three string parameters and a fourth parameter that indicates how it should display the window containing the executed application. The three strings represent the name of the file, any command-line parameters to pass to the application, and the directory to use as the startup directory.

The last parameter should be one of Windows' constants used for the ShowWindow API function. For example, SW_SHOW shows the window normally, SW_SHOWMINIMIZED shows the window initially as an icon, and so on.

Example

A file list box contains all the information you need to execute a file in the list. The following code shows a simple use of ExecuteFile to execute the currently selected file in the file list box:

procedure TFMForm.Open1Click(Sender: TObject);
begin
  with FileList do
    ExecuteFile(FileName, '', Directory, SW_SHOW);
end;

Of course, it doesn't always make sense to execute a file. If the selected item is a directory, for example, instead of executing it, you probably want to open it, or change to that directory.

The following code shows another version of Open1Click that handles directories differently from files:

procedure TFMForm.Open1Click(Sender: TObject);
begin
  with FileList do
  begin

   if HasAttr(FileName, faDirectory) then
      DirectoryOutline.Directory := FileName

   else ExecuteFile(FileName, '', Directory, SW_SHOW);
  end;
end;


See also

Selecting files to open

Saving files

TSavePictureDialog

TClipboard


Using OLE Automation

Section topics

OLE Automation is a protocol by which one application can access an object that resides inside another application or DLL. Access to this object allows you to:

Control the actions of an application or DLL.

Access the features of an application or DLL.

An application that can be automated is called an automation server. An application that automates another is an automation controller or client. An application can be simultaneously both a server and a controller.

These topics show how to use high-level Delphi objects to create OLE Automation clients. Creating OLE Automation clients is not difficult. However, this technology has a number of implications that make the subject more complicated than it at first appears. As a result, these topics will also explain some of the key architectural guidelines needed to make this technology work on a large scale.

The following subjects are covered:

Creating OLE objects with the CreateOleObject function

Using variants to encapsulate OLE objects

OLE clients

Variant arrays


About OLE Automation

Section topics

OLE Automation allows you to access objects that reside not in only your own program, but in other programs that reside on your system. In particular, you can access methods and properties of these objects, but not their raw data. You can do this without regard to the programming language used to implement the object. Network OLE allows programmers to extend this same functionality so that it works across networked machines, allowing you to create partitioned applications.

There are two major types of OLE Automation:

OLE Automation servers

OLE Automation controllers (or clients)

Automation servers provide functionality that can be accessed by automation controllers. In other words, the application or DLL that hosts an object is called a server, and the application or DLL that accesses it is called a controller. Delphi gives you the flexibility to integrate applications and DLLs with a variety of applications as either OLE Automation servers or a controllers.

Classic examples of an OLE Automation servers are Microsoft Word and Excel. Both of these applications can be controlled by Delphi applications, or by other automation controllers. For more information on OLE Automation servers, see the Developer's Guide.


A simple OLE Automation client

Section topics

The classic example of OLE Automation involves controlling Microsoft Word. If you have Word 6.X or Word 7.X on your system, then you can access it from a Delphi application by entering the following code:

uses OleAuto;

procedure TForm1.Button1Click(Sender: TObject);

var
  V: Variant

begin

 V := CreateOleObject('Word.Basic');

 V.Insert('Hello from Delphi');

end;

This code will insert the words 'Hello from Delphi' into an existing Word document. You must have Word up and running with an open document in it for this code to work. Later you'll see how to handle the situation where Word is not already up and running.

There are three key elements to the code shown above. The first is the OleAuto unit listed in the uses clause. This unit has all the key code for handling automation inside of Delphi applications.

Variants were introduced into Object Pascal because Microsoft uses them extensively in OLE Automation based code. They are not a necessity for automation, but they make it much easier, given the OLE implementation provided by Microsoft:

V := CreateOleObject('Word.Basic');

The code shown above assigns an OLE object to the variant V. The OLE object in question resides inside Word. In particular, it is an OLE Automation server that resides inside Word.

The third key element in the code is the call to the Word Basic Insert method:

V.Insert('Hello from Delphi');

Insert is not an Object Pascal method or function, nor is it part of the Windows API. It's part of Word, and you are able to call it directly from inside of a Delphi application thanks to OLE Automation.


Variants as OLE objects

Section topics

You can find out what kind of variant you have by either typecasting the variant as a TVarData record and checking its VType field, or by calling the VarType function. In particular, if a variant is an OLE object, then its VType field will be set to varDispatch.

The following code fragment shows how to verify the type of a variant object:

V := CreateOleObject('Word.Basic');

if VarType(V) = VarDispatch then

 ShowMessage('Yes')

else

 ShowMessage('No');

It would pop up the word "No" if you tried to compare the VType field from TVarData with varByte, varString, etc. In particular, what you are getting back is an oblique reference to an object of type IDispatch. If you want to get the IDispatch object itself, which is not recommended, here is how to proceed:

 A := VarToInterface(V);

 A.AddRef;

 A.GetTypeInfoCount(i);

 ShowMessage(IntToStr(i));

 A.Release;

VarToInterface is implemented in the OleAuto unit. In particular, it first checks to see if the variant does in fact contain a valid IDispatch interface. If it does, it returns the interface by executing the following line of code:

Result := TVarData(V).VDispatch;

The code fragment shown above then goes on to call GetTypeInfoCount which is a method of IDispatch which usually returns the value 1. This line of coded is added simply to show that you really do have a handle to an IDispatch interface.

Another function closely related to VarToInterface is called VarToAutoObject. This routine returns not an IDispatch interface, but the Delphi object used for manipulating IDispatch objects.

It is possible for a variant to be set to the value UnAssigned, or to Null. For instance, the following code is valid:

var

 MyVariant: Variant;

begin

 MyVariant := CreateOleObject('Word.Basic');

 ...

 MyVariant := UnAssigned;

end;

The code shown here first obtains an OLE object and then releases it. You might use code like this to free an object if you find that it is in an error state, or if you no longer need its services.

UnAssigned is a variable of type Variant that is preset to varEmpty during the initialization for the System unit.


OLE Automation and the registry

Section topics

When you call CreateOleObject, you pass it a string. This string contains something called a program ID or ProgID. Word.Basic is the ProgID for the OLE Automation server in Microsoft Word. A program ID is a string which can be looked up in the registry, and which references a CLSID. CLSIDs are statistically unique numbers that can be used by the operating system to reference an OLE Object.

When you call CreateOleObject Windows looks to see if Word is loaded in memory, if it is not, it launches it, passing the command line shown above. It then finds a interface (pointer) to the OLE Automation object requested, and it returns it in the form of the result of the call to CreateOleObject. Once again, you are not being returned a pointer to Word itself, but only a pointer to an object that resides in Word.

Once you have an interface to the OLE Automation object in Word, you can start calling the functions made available by the OLE Automation object. In particular, any of the Word Basic functions found in the Word online help are available to you. There are some 200 of these functions, and they include commands for opening documents, saving documents, formatting documents, printing documents, etc.


Running an OLE Automation server in the background

Section topics

If you want to insert something into a document when Word is shut down, then you could use the following code:

procedure TForm1.Button1Click(Sender: TObject);

begin

 V := CreateOleObject('Word.Basic');

 V.FileNew('Normal');

 V.Insert('The mark of a healthy mind' + #13);

 V.Insert('is freedom from its own ideas.');

 V.FileSaveAs('C:\SEMPERFI.DOC');

end;

The interesting thing about this code is that it never causes Word to appear on the screen. Instead, Word is launched temporarily into memory, but stays invisible. The moment the variable V goes out of scope, or the moment it is set to varNothing, Word will be shut down again. Besides the sound of your hard drive churning, the only way to tell that the above code works is to actually launch Word after a run of your application and attempt to open SEMPERFI.DOC and see if it has the proper contents.

The above code starts by calling CreateOleObject. This line will launch Word into memory if it is not already started. If it is not started, it launches it, but passes the /Automation parameter to it. Word knows that when it is started with this parameter then it should run quietly in the background without ever becoming visible. It will close itself down as soon as it no longer has an OLE Automation client to service.

Note that you can add a carriage return to these lines by inserting a line feed character (#13) into the text.

It is not mere coincidence that Word runs in the background when called into memory by an OLE Automation client. In fact, it is part of the usual definition of a server that it should run silently in the background unless specifically invoked by a user, rather than by an OLE Automation controller.


OLE Parameters: named and positional

Section topics

The FileNew and FileSaveAs commands are standard Word Basic procedures which you can look up in the Word help. The functionality of these commands is self explanatory. However, if you look up FileSaveAs in the Word help, you will find the following listing for it:

FileSaveAs [.Name = text] [, .Format = number] [, .LockAnnot = number] [, .Password = text] [, .AddToMru = number] [, .WritePassword = text] [, .RecommendReadOnly = number] [, .EmbedFonts = number] [, .NativePictureFormat = number] [, .FormsData = number] [, .SaveAsAOCELetter = number]

As you can see, FileSaveAs takes a total of 11 parameters, called, respectively, Name, Format, LockAnnot, Password, AddToMru, WritePassword, RecommendReadOnly, EmbedFonts, NativePictureFormat, FormsData and SaveAsAOCELetter. If FileSaveAs takes 11 parameters, how come the example call to this function passes in only one parameter?

The issue here is that Word supports optional parameters. The above code passes in only one parameter to the object. You could, if you wanted, pass in two parameters:

V.FileSaveAs('C:\SEMPERFI.TXT', 3);

The code shown here takes advantage of the fact that FileSaveAs takes a Format specification in the second parameter. The Format parameter can have one of the following values:

Parameter	Meaning
0	Word document
1	Document template
2	Text Only
3	Text Only with Line Breaks
4	MS-DOS Text (extended characters saved in IBM® PC character set)
5	MS-DOS Text with Line Breaks
6	Rich-Text Format

It is not necessary for you to define parameters in a particular order. For instance, you could write:

V.FileSaveAs('C:\SEMPERFI.DOC', Password := 'Sam');

This saves SEMPERFI.DOC in Word format, and assigns it "Sam" as a password. The next time you try to open this document, you must supply the password.

The above line of code is an example of using a named parameter. In particular, you are naming the 'Password' parameter. Password is normally the fourth parameter passed to this function, but you can place it in the second position if you call it by name. The following line of code has the exact same effect as the code shown above:

V.FileSaveAs(Password := 'Sam', Name := 'C:\SEMPERFI.DOC');

This code demonstrates that named parameters allow you to not only skip parameters, but to actually change the order in which you use them.

Besides named parameters, Word also supports positional parameters.

V.FileSaveAs('C:\SEMPERFI.DOC',,,'Sammy');

In the code shown above, the password "Sammy" is added to SEMPERFI.DOC. Word knows that you want "Sammy" to be a password because it resides in the fourth position. In this case the second and third parameters use default values since they are left blank.


Variant arrays

Section topics

Delphi allows you to create variant arrays which are the Delphi version of the "safe arrays" used in OLE Automation. Variant arrays are Delphi's implementation of safe arrays.

Variant arrays (and safe arrays) are costly in terms of memory and CPU cycles, so you would not normally use them except in OLE Automation code, or in special cases where they provide obvious benefits over standard arrays. For instance, the database code makes some use of variant arrays.

The OLE literature refers to these arrays as safe arrays because they contain information about the number of dimensions they contain, and about the bounds of each dimension. The Windows file called OLEAUTO32.DLL contains a whole series of SafeArrayX calls for manipulating these arrays.

Delphi encapsulates the SafeArrayX calls in several functions. The most important of these are VarArrayCreate and VarArrayOf. These functions are used to create variant arrays.

The declaration for VarArrayCreate looks like this:

function VarArrayCreate(const Bounds: array of Integer; VarType: Integer): Variant;

The Bounds parameter defines the dimensions of the array and the VarType parameter defines the type of variable stored in the array. A one-dimensional array of variants would be allocated like this:

MyVariant := VarArrayCreate([0, 5], varVariant);

This array has six elements in it, where each element is a variant. You can assign a variant array to one or more of the elements in this array. That way you can have arrays within arrays within arrays, if you so desire.

If you know the type of the elements to be used in an array, then you can set the VarType parameter to that type. For instance, if you knew you were going to be working with Integers, you could write:

MyVariant := VarArrayCreate([0, 5], varInteger);

You can not use varString in the second parameter, but should instead use varOleStr. Remember that an array of variant will take up 16 bytes for each member of the array, while other types take up much less space.

Arrays of variant can be resized with the VarArrayRedim function:

procedure VarArrayRedim(var A: Variant; HighBound: Integer);

The variable to be resized is passed in the first parameter, and the number of elements contained in the array is held in the second parameter.

A two dimensional array would be declared like this:

MyVariant := VarArrayCreate([0, 5, 0, 5], varVariant);

This array has two dimensions, each with 6 elements in it. To access a member of this array you would write code that looks like this:

procedure TForm1.GridClick(Sender: TObject);

var

 MyVariant: Variant;

begin

 MyVariant := VarArrayCreate([0, 5, 0, 5], varVariant);

 MyVariant[0, 1] := 42;

 Form1.Caption := MyVariant[0, 1];

end;

Notice that the array performs type conversions for you, since it is an array of variants, and not an array of integer.

You can use the VarArrayOf routine to quickly construct a one dimensional variant array:

function VarArrayOf(const Values: array of Variant): Variant;

The function internally calls VarArrayCreate and passes an array of Variant in the first parameter and varVariant in the second parameter. Here is a typical call to VarArrayOf:

V := VarArrayOf([1, 2, 3, 'Total', 5]);

The following code fragment shows how to use the VarArrayOf function.

procedure TForm1.ShowInfo(V: Variant);

begin

 Caption := V[3];

end;

procedure TForm1.Button1Click(Sender: TObject);

var

 V: Variant;

begin

 V := VarArrayOf([1, 2, 3, 'Four', 5]);

 ShowInfo(V);

end;

This code would print the word "Four" in the caption of Form1.

The ShowInfo function demonstrates how to work with a variant array passed to you by an OLE function or some other routine. Notice that you don't have to do anything special to access a variant as an array. If you tried to pass a variant with a VType of varInteger to this function, then an exception would be raised when you tried to treat the variant as an array. In short, the variant must have a VType  of VarArray or the call to ShowInfo will fail. You can use the VarType function to check the current setting for the VType of a variant, or you can call VarIsArray, which returns a Boolean value.

You can use the VarArrayHighBound, VarArrayLowBound, and VarArrayDimCount functions to find out about the number of dimensions in your array, and about the bounds of each dimension. The following function will be pop up a MessageBox showing the number of dimensions in a variant array, as well as the high and low values for each dimension:

procedure TForm1.ShowInfo(V: Variant);

var

 Count, HighBound, LowBound, i: Integer;

 S: string;

begin

 Count := VarArrayDimCount(V);

 S := #13 + 'DimCount: ' + IntToStr(Count) + #13;

 for i := 1 to Count do

 begin

   HighBound := VarArrayHighBound(V, i);

   LowBound := VarArrayLowBound(V, i);

   S := S + 'HighBound: ' + IntToStr(HighBound) + #13;

   S := S + 'LowBound: ' + IntToStr(LowBound) + #13;

 end;

 ShowMessage(S);

end;

This routine starts by getting the number of dimensions in the array. It then iterates through each dimension, retrieving its high and low values. If you created an array with the following call:

MyVariant := VarArrayCreate([0, 5, 1, 3], varVariant);

Then the ShowInfo function would produce the following output if passed MyVariant:

DimCount: 2

HighBound = 5

LowBound  = 0

HighBound = 3

LowBound = 1

ShowInfo would raise an exception if you passed in a variant which would cause VarIsArray to return False.

There is a certain amount of overhead in working with variant arrays. If you want to process the arrays quickly, you can use two functions called VarArrayLock and VarArrayUnlock. The first of these routines returns a pointer to the data stored in an array. In particular, VarArrayLock takes a variant array and returns a standard Object Pascal array. For this to work, the array must be explicitly declared with one of the standard types such as Integer, Bool, string, Byte or Float. The type used in the variant array and the type used in the Object Pascal array must be identical in all their members.

Here is an example of using VarArrayLock and VarArrayUnlock:

const

 HighVal = 12;

function GetArray: Variant;

var

 V: Variant;

 i, j: Integer;

begin

 V := VarArrayCreate([0, HighVal, 0, HighVal], varInteger);

 for i := 0 to HighVal do

   for j := 0 to HighVal do

     V[j, i] := i * j;

 Result := V;

end;

procedure TForm1.LockedArray1Click(Sender: TObject);

type

 TData = array[0..HighVal, 0..HighVal] of Integer;

var

 i, j: Integer;

 V: Variant;

 Data: ^TData;

begin

 V := GetArray;

 Data := VarArrayLock(V);

 for i := 0 to HighVal do

   for j := 0 to HighVal do

      Grid.Cells[i, j] := IntToStr(Data^[i, j]);

 VarArrayUnLock(V);

end;

Notice that this code first locks down the array, then accesses it as a pointer to a standard array. Finally it releases the array when the operation is finished. You must remember to call VarArrayUnlock when you are finished working with the data from the array:

 Data := VarArrayLock(V);

 for i := 0 to HighVal do

   for j := 0 to HighVal do

One of the most useful reasons for using a variant array is to transfer binary data to and from a server. If you have a binary file, say a WAVor AVI file, you can pass it back and forth between your program and an OLE server using variant arrays. Such a situation would present an ideal time for using VarArrayLock and VarArrayUnlock. You would, of course, use VarByte as the second parameter to VarArrayCreate when you were creating the array. That is, you would be working with an array of byte, and accessing it directly by locking down the array before moving data in and out of the structure. Such arrays are  not subject to translation while being marshaled across boundaries.

Remember that variant arrays are of use only in special circumstances. They are very useful tools, especially when making calls to OLE Automation objects. However, they are slower and bulkier than standard Object Pascal arrays, and should be used only when necessary.


Creating an MDI application

Section topics

These topics present the steps involved in building a simple multiple document interface (MDI) application that allows users to open child windows within the main application window. Users of an MDI application can open new or existing files from the main window, print from the document windows, and manipulate them with the standard Windows dialog boxes.

Delphi forms encapsulate the Windows MDI standard fully, so you can get a full-fledged MDI application up and running with very little coding.

To see a simple completed MDI application, open the project file TEXTEDIT.DPR, found in the DEMOS\DOC\TEXTEDIT directory if you installed Delphi using default directories. Here is the application as it appears the first time a new window is opened:

Note:	Instead of starting from scratch, you can begin your application by using the MDI template from the New Items dialog box (File|New).


About MDI applications

Section topics

MDI is an interface standard for applications that simultaneously open and display two or more files, or documents, inside a single frame window. For example, you might use MDI to allow a word-processing application to open and display several documents simultaneously and provide the user with a way to quickly switch among those open documents.

MDI applications provide a convenient way to view and exchange data among multiple documents that share a common workspace. The common workspace is called the frame window. In a Delphi MDI application, the frame window is always the application's main form.

Within the frame window, other windows, called child windows, contain the documents. These documents can all be of the same type, or they can be a mix of file types. MDI places no constraints on what goes in the child windows; MDI only covers managing and arranging the child windows within the frame.

These topics describe the basic steps in creating an MDI application:

Creating the MDI frame form

Creating the MDI child form

About MDI application menus

Grouping menus for merging

Creating child windows at runtime

Manipulating child windows


Creating the MDI frame form

Section topics

In an MDI application, the frame window provides a workspace for the application documents, which open inside one or more child windows. Creating a frame form is the first step in building the MDI application.

All Delphi forms have a FormStyle property that determines whether the form is a frame or child form (or a non-MDI application form). There can be only one MDI frame form per application, and it must be the application's main form.

To create an MDI frame form:

1.	Select the main form.

2.	Set the form's FormStyle property to fsMDIForm.

Caution:

You can set any form's FormStyle property to fsMDIForm, but your application will not compile correctly if you do so for a form that is not the application's main form. To change the application's main form, choose Options|Project and select the Forms page.


Creating the MDI child form

Section topics

You create child forms at design time as templates for each type of child window the user can open at runtime. The user can then open multiple instances of each child-window type. As soon as an MDI application creates an instance of a child form, the instance appears in the frame form's workspace.

A text editor application, for example, creates an instance of the text-editor (child) form every time the user chooses File|New. Each child-window instance reads its initial state from the same form file.

To create an MDI child form:

1.	Add a new, blank form to the project.

2.	Set the new form's FormStyle property to fsMDIChild.

By default, Delphi forms have an instance created when the application starts. To change this, see "Creating child windows."

Note:	In general, you should remove the MDI child form from the Auto-Create Forms list on the Forms page of the Project Options dialog box. This is because true MDI applications create multiple instances of a single child form at runtime. If, on the other hand, your application uses only one instance of each of several different types of child forms, as might be the case in a database application, you probably don't need to create each child form at runtime. In this case, you needn't remove the forms from the Auto-Create list. For more information about the Auto-Create Forms list, see "Specifying forms to auto-create."


About MDI application menus

Section topics

To create menus for an MDI application's forms, you use menu components and the Menu Designer, just as you would for an application with a single form. For the MDI application, however, you must address the additional issue of how menu items on the frame form and the child form interact on the shared menu bar.

By default, the menu bar for the child window that has focus (if any) replaces the frame window's menu bar. A more elegant solution is to merge the items specific to the focused child window with those on the frame window that apply to any window. This is called menu merging.

The items on the frame form's menu should either manipulate child windows or work when there are no child windows open. The most common frame-form menu items are a File menu for opening child windows and a Window menu for manipulating them. The File and Window menu items exist in the frame form, because they control functions of the frame, rather than functions specific to a particular child window.

When the user opens a child window, the application merges items from the child form's menu into the main menu bar.

You control the order of merged menu items by setting the value of the GroupIndex property for each menu item in each menu. In fact, you can use the GroupIndex property to replace a main menu item with a child menu item, instead of inserting child menu items into the main menu. For example, if you assign a GroupIndex value of 0 to the File menu item on both the frame form and the child form, this causes the child's version of File to completely replace the frame form's version when the user opens a child window. (The value zero also ensures that the File menu always appears leftmost.)


Creating the frame-form menu

Section topics

The frame form's menu bar serves as the main menu for an MDI application.

To create a frame-form menu:

1.	Add a main menu component to the frame form.

2.	Use the Menu Designer to add items to the menu.


Creating a child-form menu

Section topics

MDI child-form menus provide whatever special functions the child window needs. For a child window that contains a text editor, for example, users need menu items to manipulate text in the window. If the child window contains a graphic image, the menu items might allow resizing, coloring, and so on.

To create an MDI child-form menu:

1.	Add a main menu component to the child form.

2.	Use the Menu Designer to add items to the menu.


Grouping menus for merging

Section topics

One of the characteristics of MDI applications is that they can change their menus depending on which child window has the focus. If the application has no child windows open, the main menu is the frame form's menu. A child window can either add its menu items to the main menu or replace existing items. This is called merging menus.

Delphi MDI applications handle menu merging automatically. Keep in mind that you can only merge entire menus of items, such as adding an Edit menu or replacing an entire File menu. You cannot merge the individual items within a File menu, for example.

You control the merging of menus by arranging the menus into groups, assigning each group an index number. The application treats all menus with the same index number as parts of a group: two or more menus that should always be inserted or replaced together should have the same index number.

To create groups of menu items:

1.	Select all the items you want grouped together.

2.	Set their GroupIndex property to an integer value, keeping in mind the following rules:

A menu's GroupIndex must be greater than or equal to that of the menu to its left. Items within a group retain their left-to-right order as designed.

A group of child-form menus will replace a group of frame-form menus with the same GroupIndex value.

The merged main menu contains the groups of menu items in ascending order of their GroupIndex values, with the lowest GroupIndex on the left.

3.	Repeat the process for each group of menus.


Handling replaced menu commands

Section topics

When a merged menu replaces another menu, it often replaces a number of similar or identical items. For example, various child windows might all add their own special items to the File menu, while maintaining standard items such as File|New and File|Exit. Rather than duplicating the event-handler code for each of these items, you can write the common code once and call it from the replacing menu items.

To share event handlers between merged menus:

1.	Attach an event handler to the frame form's menu item.

The event handler performs whatever action you want in response to that menu item.

2.	Attach an event handler to the child form's corresponding menu item.

3.	Call the frame-form event handler from the child form's handler.


Creating child windows

Section topics

Once you've created a main form and any child forms for an MDI application and set up their menus, it's time to specify how and when to create the child windows at runtime.

In most MDI applications, you don't want automatic creation of child windows. Instead, the application should create child windows on demand, such as when opening or creating a new document.

To give users full control of the application's child-window creation:

1.	Remove the child from auto-creation

2.	Create child windows at runtime


Removing the child from auto-creation

Section topics

In most cases, you won't want your MDI applications to create child-window instances automatically. By convention, MDI applications add child windows only on demand, such as when the user chooses File|New.

You will usually remove your child-window forms from the project's auto-creation list.

To remove a form from the auto-creation list:

1.	Choose Project|Options to access the Auto-Create Forms list.

2.	In the Auto-Create Forms list, select the form you do not want automatically created.

3.	Click the > button to move the form into the Available Forms list.

4.	Choose OK to exit the dialog box.


Creating child windows at runtime

Section topics

Most MDI applications allow users to open new child windows at runtime. The most common mechanism for this is a menu item, such as File|New. In that case, you would create the child window in the event handler for a click on File|New. Your application might also create child windows in response to other events or within some other code.

To create a new child window at runtime:

1.	Locate the point in your code where you want to create a child window.

This will usually be an event handler for a menu item. If necessary, generate an empty event handler for the desired menu-item click.

2.	Call the Create method of the child-window form you want an instance of, and pass the frame window as the Owner parameter to the Create method.

In the following example, the frame form's handler calls the Create method for the child form type (TEditForm). The child form's handler just calls the frame form's handler.

procedure TFrameForm.New1Click(Sender: TObject);

begin
  TEditForm.Create(Self);	{ create the new child window }

end;

procedure TEditForm.New1Click(Sender: TObject);

begin
  FrameForm.New1Click(Sender);	{ call the frame form's handler }

end;

Opening an existing file involves a bit more, and is discussed in "Selecting files to open."


Referencing identifiers in other units

Section topics

When you're compiling, Delphi generates an "Unknown identifier" error message when it encounters an object it doesn't yet "know" about. Sometimes the compiler can suggest ways to resolve errors if it has already compiled a symbol being referenced--it can suggest using that already-compiled unit. But sometimes, the compiler hasn't yet compiled the unit in question, so it can't suggest using it. You'll have to reference the unit manually.

If you want to reference anything declared in another form's unit, that unit must appear in the referencing form unit's uses clause.

To reference a unit from another form's unit:

1.	Select the form that needs to refer to another unit.

2.	Choose File|Use Unit.

The Use Unit dialog box opens.

3.	Select the unit you want to use.

4.	Choose OK to close the dialog box.

Delphi adds the selected unit to the uses clause of the current unit.


Manipulating child windows

Section topics

Once your application can respond to the File|New menu command by creating a new child window, you can begin writing code that works with open child windows. Specifically, you can attach event handlers to menu items that will close and arrange child windows. You can also add a list of open child windows to the Window menu. And you can customize the caption of each child window to reflect the name of the document it contains.


Closing child windows

Section topics

An application that can open child windows should also be able to close them. In Delphi's encapsulation of a window, clicking the close button on the window's frame or double-clicking its control-menu box or pressing Ctrl+F4 all close the window by calling a method called Close. Your application can provide other ways to close the active child window, such as choosing the File|Close menu command, by calling the same method.

To close a child window,
Call its Close method.

The default closing behavior for MDI child windows is to minimize, rather than close. This is controlled by a parameter of the OnClose event called Action. By default, Action has the value caMinimize. By setting Action to caFree, you cause the application to destroy the window and free its resources on closing.

To change the closing behavior of child windows:

1.	Attach a handler to the child form's OnClose event.

2.	Change the value of the Action parameter.


Arranging open child windows

Section topics

MDI applications should always have a Window menu that contains Tile, Cascade, and Arrange Icons commands to offer users an easy way to arrange the open documents in the frame window. You've already created the Window menu for the text editor application. Enabling the Tile, Cascade, and Arrange Icons commands requires just a few lines of code. Delphi form components have methods built into them to handle tiling, cascading, and arranging icons. All you do is call these methods in response to the corresponding menu items.

To tile, cascade, or arrange MDI child window icons,
Call the frame form's Tile, Cascade, and ArrangeIcons methods, respectively.


Providing a list of open documents

Section topics

You can append a list of open documents to any menu item that appears on a menu bar in the MDI form. However, there can be only one such list per menu bar. The list of open documents appears underneath the last item in the menu.

To include a list of open documents as part of a menu:

1.	Select the frame form, then select the Properties page of the Object Inspector.

2.	From the drop-down list next to the WindowMenu property, select the name (not the caption) of the menu you want the open document list to appear under.

This name must represent an item that appears on the menu bar, not a submenu item, because document lists cannot be used in nested menus.


Naming the child windows

Section topics

In a multiple-document setting, it's helpful to have distinct names for all the child windows. In most cases, that means displaying something like the name of the document in the caption.

Since path names can get quite long, you can display only the file-name portion in the caption, but store the entire path name in a form-level variable. For example, the following declaration, added to the private part of an EditForm type declaration uses a string-type field called PathName that is declared as part of a form-level variable called EditForm:

PathName: string;

PathName will hold the name of the file each child window contains. PathName is then used when opening and saving files and creating backup files.

Caution:	All field declarations must precede any method declarations in the private part. Thus, the PathName declaration should immediately follow the reserved word private in the above EditForm type declaration.

You also need to set an initial value for PathName, which you can do when the application first creates the child form. Since you'll need to reference the initial value later, you can declare it as a constant in the interface part of the MDI unit:

const
  DefaultFileName = 'Untitled';

You can now assign DefaultFileName as an initial value and test for it later. Here's an example:

procedure TEditForm.FormCreate(Sender: TObject);

begin

 PathName := DefaultFileName;

end;

Later, when you load files into the editor, you'll assign the actual path name to PathName, indicating that the editor contains an already-saved file.


Putting documents in child forms

Section topics

Once you've created the frame form and the child form, specified the contents of the menus for both forms and handled their run-time behavior, and enabled the basic child-window manipulation commands and features, you're ready to put documents in the child windows.

It's the contents of the child windows (or documents) that establish the functions of an MDI application. Since each MDI application is different, it's not really possible to generalize all the tasks for implementing documents. The examples that follow focus on editing text documents, partly because that is a straightforward document application, but also because text-editing tasks are relatively common in many kinds of applications. For example, cutting and pasting text with the Clipboard applies to many kinds of controls and applications.


Filling a form with a document

Section topics

There is no restriction on the contents of an MDI child window, and any Delphi form, no matter how complex, can be turned into an MDI child. The most common kind of MDI child window, however, is one completely filled by a single component that represents a document, which might be a text file, a graphic image, or a spreadsheet.

To fill a child-window form with a document:

1.	Select the child form.

2.	Place the document component on the form.

3.	Set the document component's Align property to alClient.

4.	Turn off any visible borders on the document component (for example, setting a BorderStyle property to bsNone).

You might need to set other properties on the document component as well. The idea is to make the document fill the entire child form and remove any visible distinctions between the component and the window frame.

For example, you can add a text editor as the document component using either a rich-edit component or a memo component for an editor in the child form. The rich-edit component offers both greater capacity and the ability to vary the formatting of the text. You would typically set its properties as shown in the following table:

Property	Value
Align	alClient
BorderStyle	bsNone
ScrollBars	ssVertical
Name	Editor
Lines	<empty>
.


Exiting gracefully

Section topics

Applications that enable users to modify and save data into files should include safeguards so that users do not lose their work, for example, the creation of a backup file every time the user saves an existing file. It's also important to track changes and prompt the user to save unsaved changes before closing.

Since closing the application closes each of the child windows, protecting individual window closing will also handle the case where the user closes the application with one or more child windows that contain unsaved changes.

Before closing its form, the Close method generates an OnCloseQuery event. The OnCloseQuery event has a variable Boolean parameter called CanClose. By default, CanClose is True, meaning the form can go ahead and close, including generating the OnClose event.

To keep a form from closing:

1.	Attach a handler to the form's OnCloseQuery event.

2.	Set the CanClose parameter to False. This will usually be conditional, based on whether the document has unsaved changes.

For example, the following code checks the Modified property of the sample text editor application's Editor component to see if there are unsaved changes. If there are, the MessageDlg function produces a message dialog box. The Format function incorporates the file name into the message-box prompt string. For more information about MessageDlg and Format, see online Help. This handler is attached to the OnCloseQuery event of the editor form:

procedure TEditForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);

const
  SWarningText = 'Save changes to %s?';

begin

 if Editor.Modified then	{ only prompt if there are unsaved changes }

 begin

   case MessageDlg(Format(SWarningText, [PathName]), mtConfirmation,

     [mbYes, mbNo, mbCancel], 0); of	{ produce the message dialog box }

     id_Yes: Save1Click(Self);	{ yes means user wants to save }

     id_Cancel: CanClose := False;	{ if user Cancels, exit dialog but don't close form }

   end;

 end;

end;

Note that you don't need to provide a response if the user chooses No from the message dialog box, because the default value for CanClose is True, which means the child window will close without saving the changes in the editor.


Creating international applications

Section topics	See also

These topics discuss guidelines for writing Delphi applications that you plan to distribute to an international market. By planning ahead, you can reduce the amount of time and code necessary to make your application function in its foreign market as well as in its domestic market.


Localization and internationalization

Section topics	See also

To create an application that you can distribute to foreign markets, you must perform two major steps. These are localization and internationalization.

Localization

Localization is the process of translating an application to function in a specific locale. A locale is the user's environment, which includes the cultural conventions of the target country as well as the language. Windows supports a large set of locales, each of which is described by a language and country pair.

In addition to translating the strings that appear in your user interface, localization includes translating country-specific elements such as sort order and date, time, number, and currency formats.

 Internationalization

Internationalization is the process of enabling your program to work in multiple locales. You can distribute localized versions of your application without internationalizing it, but this is an inefficient and costly approach. The time spent internationalizing your application is more than made up in the speed with which you can develop localized versions. In addition, localizing an internationalized application does not destabilize the product as much, because less code must be altered. This allows you to produce more stable localized versions with less testing. Finally, localized applications are easier to maintain, because revisions do not need to be folded into several localized versions.


Internationalizing Delphi applications

Section topics	See also

It is not very difficult to create internationalized Delphi applications. There are three main considerations: you need to isolate all resources that need to be localized, you need to design your user interface so that it can accommodate the changes that result from localization, and you must enable your code to handle strings from international character sets.


Isolating resources

Section topics	See also

The most obvious task in localizing an application is translating the strings that appear in the user interface. To create an application that can be translated without altering code everywhere, the strings in the user interface should be isolated into a single module. Delphi automatically creates a .DFM file that contains the resources for your menus, dialogs, and bitmaps.

In addition to these obvious user interface elements, you will need to isolate any strings, such as error messages, that you present to the user. Because date, time, number, and currency formats are locale-specific, you will want to isolate these as well. String resources are not included in the .DFM file, but you can isolate them into a .DRC file by declaring constants for them using the resourcestring keyword.

For more information about resource string constants, see the Object Pascal Language Guide.

It is best to include all resource string constants in a single, separate unit. By putting all resource string constants in a single unit, you need only localize a single .DRC file rather than several .DRC files scattered throughout your application.

See the Developer's Guide to learn how to localize the resources you have isolated in your .DFM and .DRC files.


Designing the user interface

Section topics	See also

When creating an application for several foreign markets, it is important to design your user interface so that it can accommodate the changes that occur during translation.

Text

All text that appears in the user interface must be translated. English text is almost always shorter than its translations. Design the elements of your user interface that display text so that there is room for the text strings to grow. Create dialogs, menus, status bars, and other user interface elements that display text so that they can easily display longer strings. Avoid abbreviations, as they do not exist in languages that use ideographic characters.

Short strings tend to grow in translation more than long phrases. The following table provides a rough estimate of how much expansion you should plan for given the length of your English strings:

Length of English String (in characters)	Expected Increase
1-5	100%
6-12	80%
13-20	60%
21-30	40%
31-50	20%
over 50	10%
Graphic images

Ideally, you will want to use images that do not require translation. Most obviously, this means that graphic images should not include text, which will always require translation. If you must include text in your images, it is a good idea to use a label object with a mtransparent background on top of an image rather than including the text as part of the image.

There are other considerations when creating graphic images. Try to avoid images that are specific to a particular culture. For example, mailboxes in different countries look very different from each other. Religious symbols are not appropriate if your application is intended for countries that have different dominant religions. Even color can have different symbolic connotations in different cultures.

Formats and sort order

The date, time, number, and currency formats used in your application should be localized for the target locale. If you use only the Windows formats, there is no need to translate formats, as these are taken from the user's Windows Registry. However, if you specify any of your own format strings, be sure to declare them as resourced constants so that they can be localized.

The order in which strings are sorted also varies from country to country. Many European languages include diacritical marks that are sorted differently, depending on the locale. In addition, in some countries, two-character combinations are treated as a single character in the sort order. For example, in Spanish, the combination ch is sorted like a single unique letter between c and d. Sometimes a single character is sorted as if it were two separate characters, such as the German eszett.

Delphi controls that sort strings allow you to specify the sorting function that compares two strings. Be sure to localize the sorting function when localizing your application.

Keyboard mappings

Be careful with key-combinations shortcut assignments. Not all the characters available on the US keyboard are easily reproduced on all international keyboards. Where possible, use number keys and function keys for shortcuts, as these are available on virtually all keyboards.


Enabling application code

Section topics	See also

In addition to isolating the resources and designing your user interface to accommodate the translated resources, you should make sure that the code in your application can handle the strings it will encounter in the various target locales.

Character sets

The Windows operating system in the US and most of Europe uses the ANSI character set. However, many eastern versions of Windows must use a different character set. For example, the Japanese version of Windows uses the Shift-Jis character set, which represents Japanese characters as one- or two-byte character codes. Delphi automatically displays strings on the screen and reads strings typed by the user using the local Windows character set.

When specifying a font in your Delphi application, you must make sure that the character set of the font is the one your code is expecting (usually the one used by the local Windows version). The TFont object has a CharSet property that allows you to control the character set of any font you use. When allowing the user to choose a font, it is a good idea to use the common font dialog which allows the user to specify the character set of fonts.

OEM and ANSI character sets

When writing applications intended for European markets, it is sometimes necessary to convert between the Windows character set (ANSI), and the character set specified by the code page of the users machine (called the OEM character set). For example, the Borland Database Engine (BDE) expects the OEM character set in its API function calls. If you use the Delphi database components, you do not need to worry about converting between OEM and ANSI character sets. These components handle all necessary conversions automatically. However, if you are making direct BDE API calls, be sure to convert between the ANSI character set used by Delphi and the OEM character set used by the BDE.

Multi byte characters sets

The ideographic character sets used in the Far East cannot use the simple 1:1 mapping between characters in the language and the one byte (8-bit) Char type. These languages have two many characters to be represented using the one byte Char. Instead, characters are represented by a mix of one- and two-byte character codes. The first byte of every two-byte character code is taken from a reserved range that depends on the specific character set. The second byte can sometimes be the same as the character code for a separate one-byte character or it can fall in the range reserved for the first byte of two-byte characters. Thus, the only way to tell whether a particular byte in a string represents a single character or part of a two-byte character is to read the string, starting at the beginning, parsing it into two-byte characters when a lead byte from the reserved range is encountered.

When writing code for far eastern locales, therefore, you must be sure to handle all string manipulation using functions that are enabled to parse strings into one- and two-byte characters. Delphi provides you with a number of runtime library functions that are enabled to work with MBCS strings. These functions are as follows:

AnsiCompareFileName	AnsiStrIComp	ByteToCharIndex	ExtractFileName
AnsiExtractQuotedSet	AnsiStrLastComp	ByteToCharLen	ExtractFilePath
AnsiLastChar	AnsiStrLComp	ByteType	IsDelimiter
AnsiLowerCase	AnsiStrLIComp	ChangeFileText	IsPathDelimiter
AnsiLowerCaseFileName	AnsiStrLower	CharToByteIndex	LastDelimiter
AnsiPos	AnsiStrUpper	CharToByteLen	StrByteType
AnsiQuotedStr	AnsiUpperCase	ExtractFileDir
AnsiStrComp	AnsiUpperCaseFileName	ExtractFileExt

When working with MBCS strings, use the ByteType or StrByteType functions to determine whether a particular byte represents the lead byte of a two-byte character. Remember that the length of the strings in bytes does not necessarily correspond to the length of the string in characters. Be careful not to truncate strings by cutting a two-byte character in half. Do not pass characters as a parameter to a function or procedure, since the size of a character can't be known up front. Instead, always pass a pointer to a character or a string. Write string-processing algorithms to parse strings from front to back, as this is much less expensive than examining the characters of a string from back to front.

Wide characters

Another approach to working with ideographic character sets is to convert all characters to a wide characters encoding scheme such as Unicode. Wide characters are two bytes instead of one, so that the character set can represent many more different characters.

Using a wide character encoding scheme has the advantage that you can make many of the usual assumptions about strings that do not work for MBCS systems. There is a direct relationship between the number of bytes in the string and the number of characters in the string. You do not need to worry about cutting characters in half or mistaking the second half of a character for the start of a different character.

Delphi supports the WideChar type, so that wide character strings can be represented as arrays of WideChars. The runtime library does not include as many wide character string functions, but most of these are easy to implement yourself. Delphi does provide you with several runtime functions to convert between standard single-byte character strings (or MBCS strings) and Unicode strings. These are:

StringToWideChar

WideCharLenToString

WideCharLenToStrVar

WideCharToString

WideCharToStrVar

The biggest disadvantage of working with wide characters is that Windows 95 does not support wide character API function calls. Because of this, the Delphi components represent all string values as single byte or MBCS strings. Translating between the wide character system and the MBCS system every time you set a string property or read its value would require tremendous amounts of extra code and slow your application down. However, you may want to translate into wide characters for some special string processing algorithms that need to take advantage of the 1:1 mapping between characters and WideChars.

Locale-specific features

You may wish to add extra features to your application for specific locales. In particular, for Asian language environments, you may wish your application to control the input method editor (IME) that is used to convert the keystrokes typed by the user into character strings.

Delphi components offer you support in programming the IME. Most windowed controls that work directly with text input have an IMEName property that allows you to specify a particular IME that should be used when the control has input focus. They also provide an IMEMode property that specifies how the IME should convert keyboard input. TWinControl introduces several protected methods that you can use to control the IME from objects classes you define. In addition, the global Screen variable provides you with information about the IMEs available on the user's system.

The global Screen variable also provides information about the keyboard mapping installed on the user's system. You can use this to obtain locale-specific information about the environment in which your application is running.


Localizing Delphi applications

Section topics	See also

Once your Delphi application is internationalized, you can create localized versions for the different foreign markets in which you want to distribute it.


Localizing resources

Section topics	See also

Delphi isolates your user interface elements into the .DFM file. This .DFM file can be converted into a text file by using the command line Convert utility that ships with Delphi. Call Convert with the name of your .DFM file as the only argument, or create a file listing several .DFM files on separate lines and call Convert specifying this file after an @ symbol.

Once you have translated the resources in the text file produced by Convert, call Convert again, giving the translated text file as the only argument. Convert creates a new, translated .DFM file from your translated text file. You can then recompile your Delphi executable, DLL, or package with the localized .DFM file to create a version of your application with a localized user interface.

Note:	You can also translate the .DFM file directly in the IDE by right clicking on your forms and choosing View As Text.

For string resources, you must compile your application using the -gd command line switch or request Detailed Map File on the linker page when you compile your application from the IDE. If you compile your application in either of these ways, it generates a .DRC file that contains tables of all string constants you declared using the resourcestring keyword. After you translate the strings in the .DRC file, use the command line BRCC32 utility that ships with Delphi to compile the .DRC file into a resource (.RES) file. Link this localized .RES file with your executable, DLL, or package to complete the process of localizing your application.


Creating resource modules

Section topics	See also

You can create separate resource modules for each locale where you want to ship your application by stripping the resources isolated in your .DFM and .DRC files from your application into a separate resource module.

To create separate resource modules for each locale:

1.	Obtain the two or three character locale code for the target market of your localized application. You can get this locale code by using the global GetLocaleInfo function and specifying LOCALE_SABBREVLANGNAME as the second parameter. The first two characters indicate the target language, and the last character stands for the country of the locale.

2.	Create a new library by choosing File|New and choosing a DLL.

3.	Include your localized resource files as the only thing in the new DLL. For example, if your application is MyApp.exe, and you have localized the interface to create MyApp.dfm and localized the strings resources to create MyApp.res, you would add the following lines to the new library:

{$R MyApp.res}

{$R MyApp.dfm}

You must give the full name of the .DFM or .RES file. Do not leave off the unit name by specifying the file with a string such as *.DFM.

4.	Give the library the same name as your application's executable, DLL, or package. Give it the two or three character locale code you obtained in step 1 as an extension.

Create resource modules for every executable, DLL, or package that needs to be localized.


Using resource modules

Section topics	See also

The executable, DLLs, and packages that make up your application contain all the resources they were compiled with. However, to replace those resources by localized versions, you need only ship your application with localized resource modules that have the same name as your .EXE, .DLL, or .DPL files.

When your Delphi application starts up, it checks the locale of the local system. If it finds any resource modules with the same name as the .EXE, .DLL, or .DPL files it is using, it checks the extension on those modules. If the extension of the resource module matches the language and country of the system locale, your application will use the resources in that resource module instead of the resources in the executable, DLL, or package. If there is not resource module that matches both the language and the country, your application will try to locate a resource module that matches just the language. If there is not resource module that matches the language, your application will use the resources compiled with the executable, DLL, or package.

If you want your application to use a different resource module than the one that matches the locale of the local system, you can set a locale override entry in the Windows registry.  Under the HKEY_Current_User\Software\Borland\Delphi\Locales key, add your application’s path and filename as a string value and set the data value to the extension of your resource modules.  At startup, the application will look for resource modules with this extension before trying the system locale.

Within your application, use the global FindResourceHInstance function to obtain the handle of the current resource module.

Thus, you can ship a single application that adapts itself automatically to the locale of the system it is run on, simply by providing the appropriate resource modules.


See also

Form files

Resource files

Other files

Using the Menu designer

Creating an owner-draw control

Working with graphics files

Format strings

TFont

TScreen


The command-line compiler

Section topics	See also

Delphi's command-line compiler (DCC32.EXE) lets you invoke all the functions of the IDE compiler (DELPHI.EXE) from the DOS command line. Run the command-line compiler from the DOS prompt using the syntax:

DCC32 [options] filename [options]

where options are zero or more parameters that provide information to the compiler and filename is the name of the source file to compile. If you type DCC32 alone, it displays a help screen of command-line options and syntax.

If filename does not have an extension, the command-line compiler assumes .DPR, then .PAS if no .DPR is found. If the file you're compiling to doesn't have an extension, you must append a period (.) to the end of the filename.

If the source text contained in filename is a program, the compiler creates an executable file named filename.EXE. If filename contains a library, the compiler creates a file named filename.DLL. If filename contains a package, the compiler creates a file named filename.DPL. If filename contains a unit, the compiler creates a unit file named filename.DCU.

You can specify a number of options for the command-line compiler. An option consists of a slash (/) immediately followed by an option letter. In some cases, the option letter is followed by additional information, such as a number, a symbol, or a directory name. Options can be given in any order and can come before or after the file name.


Command-line compiler options

Section topics	See also

The IDE lets you set various options through the menus; the command-line compiler gives you access to these options using the slash (/) delimiter. You can also precede options with a hyphen (-) instead of a slash (/), but those options that start with a hyphen must be separated by blanks. For example, the following two command lines are equivalent and legal:

DCC -IC:\DELPHI -DDEBUG SORTNAME -$R- -$U+

DCC /IC:\DELPHI/DDEBUG SORTNAME /$R-/$U+

The first command line uses hyphens with at least one blank separating options. The second uses slashes and no separation is needed.

The following table lists the command-line options. In addition to the listed options, all single-letter compiler directives can be specified on the command line, as described in the next topic.

Option	Description
/Aunit=alias	Set unit alias
/B	Build all units
/CC	Console target
/CG	GUI target
/Ddefines	Define conditional symbol
/Epath	EXE directory
/Faddress	Find run-time error
/GS	Map file with segments
/GP	Map file with publics
/GD	Detailed map file
/H	Output hint messages
/Ipaths	Include directories
/J	Generate OBJ file
/JP	Generate C++ OBJ file
/Kaddress	Set image base address
/LEpath	Package DPL directory
/LNpath	Package DCP directory
/LUpackage	Use packages
/M	Make modified units
/Npath	DCU directory
/Opaths	Object directories
/P	Look for 8.3 file names also
/Q	Quiet compile
/Rpaths	Resource directories
/TXext	Target file extension
/Upaths	Unit directories
/V	Turbo Debugger debug information
/W	Output warning messages
/Z	Disable implicit compilation

If you type DCC32 alone at the command line, a list of command-line compiler options appears on your screen.


Compiler directive options

Section topics	See also

Delphi supports several compiler directives, all described in "Compiler directives." The /$ and /D command-line options allow you to change the default states of most compiler directives. Using /$ and /D on the command line is equivalent to inserting the corresponding compiler directive at the beginning of each source file compiled.

The switch directive option

The /$ option lets you change the default state of all of the switch directives. The syntax of a switch directive option is /$ followed by the directive letter, followed by a plus (+) or a minus (-). For example,

DCC32 MYSTUFF /$R-

compiles MYSTUFF.PAS with range-checking turned off, while

DCC32 MYSTUFF /$R+

compiles it with range checking turned on. Note that if a {$R+} or {$R-} compiler directive appears in the source text, it overrides the /$R command-line option.

You can repeat the /$ option in order to specify multiple compiler directives:

DCC32 MYSTUFF /$R-/$I-/$V-/$U+

Alternately, the command-line compiler lets you write a list of directives (except for $M), separated by commas:

DCC32 MYSTUFF /$R-,I-,V-,U+

Only one dollar sign ($) is needed.

Note that, because of its format, you cannot use the $M directive in a list of directives separated by commas.

The conditional defines option

The /D option lets you define conditional symbols, corresponding to the {$DEFINE symbol} compiler directive. The /D option must be followed by one or more conditional symbols separated by semicolons (;). For example, the following command line

DCC32 MYSTUFF /DIOCHECK;DEBUG;LIST

defines three conditional symbols, iocheck, debug, and list, for the compilation of MYSTUFF.PAS. This is equivalent to inserting

{$DEFINE IOCHECK}

{$DEFINE DEBUG}

{$DEFINE LIST}

at the beginning of MYSTUFF.PAS. If you specify multiple /D directives, you can concatenate the symbol lists. Therefore,

DCC32 MYSTUFF /DIOCHECK/DDEBUG/DLIST

is equivalent to the first example.


Compiler mode options

Section topics	See also

A few options affect how the compiler itself functions. As with the other options, you can use these with either the hyphen or the slash format. Remember to separate the options with at least one blank.

The make (/M) option

The command-line compiler has built-in MAKE logic to aid in project maintenance. The /M option instructs command-line compiler to check all units upon which the file being compiled depends. Using this option results in a much quicker compile time.

A unit will be recompiled if:

The source file for that unit has been modified since the unit file was created.

Any file included with the $I directive, any .OBJ file linked in by the $L directive, or any .RES file referenced by the $R directive, is newer than the unit file.

The interface section of a unit referenced in a uses statement has changed.

Units compiled with /Z option are excluded from the make logic.

If you were applying this option to the previous example, the command would be

DCC32 MYSTUFF /M

The build all (/B) option

Instead of relying on the /M option to determine what needs to be updated, you can tell command-line compiler to update all units upon which your program depends using the /B option. You can't use /M and /B at the same time. The /B option is slower than the /M option and is usually unnecessary.

If you were using this option in the previous example, the command would be

DCC32 MYSTUFF /B

The find error (/F) option

When a program terminates due to a runtime error, it displays an error code and the address at which the error occurred. By specifying that address in a /Faddress option, you can locate the statement in the source text that caused the error, provided your program and units were compiled with debug information enabled (via the $D compiler directive).

In order for the command-line compiler to find the run-time error with /F, you must compile the program with all the same command-line parameters you used the first time you compiled it.

As mentioned previously, you must compile your program and units with debug information enabled for the command-line compiler to be able to find run-time errors. By default, all programs and units are compiled with debug information enabled, but if you turn it off, using a {$D-} compiler directive or a /$D- option, the command-line compiler will not be able to locate run-time errors.

The use packages (/LU) option

Use the z/LU option to list additional runtime packages that you want to use in the application being compiled. Runtime packages already listed in Delphi's Project Options dialog need not be repeated on the command line.

The disable implicit compilation (/Z) option

The /Z option prevents packages and units from being implicitly recompiled later. With packages, it is equivalent to placing {$ IMPLICITBUILD OFF} in the .DPK file. Use /Z when compiling packages that provide low-level functionality, that change infrequently between builds, or whose source code will not be distributed.

The target file extension (/TX) option

The /TX option lets you override the default extension for the output file. For example,

DCC32 MYSTUFF /TXSYS

generates compiled output in a file called MYSTUFF.SYS.

The quiet (/Q) option

The quiet mode option suppresses the printing of file names and line numbers during compilation. When the command-line compiler is invoked with the quiet mode option

DCC32 MYSTUFF /Q

its output is limited to the startup copyright message and the usual statistics at the end of compilation. If any errors occur, they will be reported.


Directory options

Section topics	See also

Several options allow you to specify the directory lists used by the command-line compiler: include (/I), DCU input (unit search path, /U), resource (/R), object (/O), EXE and DCU output (/E), and DCU output (/N).

You can specify multiple directories, separated by semicolons, for some options. For example, this command line tells the command-line compiler to search for include files in C:\DELPHI\INCLUDE and D:\INC after searching the current directory:

DCC32 MYSTUFF /IC:\DELPHI\INCLUDE;D:\INC

If you specify multiple directives, the directory lists are concatenated. Therefore,

DCC32 MYSTUFF /IC:\DELPHI\INCLUDE /ID:\INC

is equivalent to the first example.

The EXE directory (/E) option

This option lets you tell the command-line compiler where to put the .EXE file it creates. It takes a directory path as its argument:

DCC32 MYSTUFF /EC:\DELPHI\BIN

You can specify only one EXE directory, which is also used for .DLL files. The /E option does not affect the location of .DPL (package) files.

If no such option is given, the command-line compiler creates .EXE files in the same directories as their corresponding source files.

The package DPL directory (/LE) option

This option lets you tell the command-line compiler where to put the .DPL file it creates when it compiles a package. The syntax is the same as /E.

The package DCP directory (/LN) option

This option lets you tell the command-line compiler where to put the .DCP file it creates when it compiles a package. The syntax is the same as /E.

The DCU directory (/N) option

This option lets you tell the command-line compiler where to put the .DCU files it creates when it compiles a unit. The syntax is the same as /E.

The include directories (/I) option

Delphi supports include files through the {$I filename} compiler directive. The /I option lets you specify a list of directories in which to search for include files.

The unit directories (/U) option

When you compile a program that uses units, the command-line compiler searches for unit files in the current directory. The /U option lets you specify additional directories in which to search for units.

The resource directories (/R) option

DCC32 searches for resource files in the current directory. The /R option lets you indicate additional directories where DCC32 should look for resource files.

The object files directories (/O) option

Using {$L filename} compiler directives, Delphi lets you link in .OBJ files containing machine code created by external assemblers or other compilers, such as Borland C++. The /O option lets you specify a list of directories in which to search for such .OBJ files.


Debug options

Section topics	See also

The command-line compiler has two command-line options that enable you to generate external debugging information: the map file option and the Turbo Debugger debug info option.

The map file (/G) option

The /G option instructs the command-line compiler to generate a .MAP file that shows the layout of the .EXE file. Unlike the binary format of .EXE and .DCU files, a .MAP file is a legible text file that can be output on a printer or loaded into the editor. The /G option must be followed by the letter S, P, or D to indicate the desired level of information in the .MAP file. A .MAP file is divided into three sections:

Segment

Publics

Line Numbers

The /GS option outputs only the Segment section, /GP outputs the Segment and Publics section, and /GD outputs all three sections.

For modules (program and units) compiled in the {$D+,L+} state (the default), the Publics section shows all global variables, procedures, and functions, and the Line Numbers section shows line numbers for all procedures and functions in the module. In the {$D+,L-} state, only symbols defined in a unit's interface part are listed in the Publics section. For modules compiled in the {$D-} state, there are no entries in the Line Numbers section.

The generate Turbo Debugger debug info (/V) option

When you specify the /V option on the command line, the command-line compiler appends Turbo Debugger 5.0-compatible external debug information at the end of the .EXE file. Turbo Debugger includes both source- and machine-level debugging and powerful breakpoints.

Even though the debug information generated by /V makes the resulting .EXE file larger, it does not affect the actual code in the .EXE file, and does not require additional memory to run the program.

The extent of debug information appended to the .EXE file depends on the setting of the $D and $L compiler directives in each of the modules (program and units) that make up the application. For modules compiled in the {$D+,L+} state, which is the default, all constant, variable, type, procedure, and function symbols are known to the debugger. In the {$D+,L-} state, only symbols defined in a unit's interface section are known to the debugger. In the {$D-} state, no line-number records are generated, so the debugger cannot display source lines when you debug the application.

The IDE internal debugger does not use Turbo Debugger debug information. Because generating Turbo Debugger debug information almost doubles compile/link time, you should turn off Turbo Debugger debug information generation except when you're debugging the application in Turbo Debugger.


The DCC32.CFG file

Section topics	See also

You can set up a list of options in a configuration file called DCC32.CFG, which will then be used in addition to the options entered on the command line. Each line in configuration file corresponds to an extra command-line argument inserted before the actual command-line arguments. Thus, by creating a configuration file, you can change the default setting of any command-line option.

The command-line compiler lets you enter the same command-line option several times, ignoring all but the last occurrence. This way, even though you've changed some settings with a configuration file, you can still override them on the command line.

When DCC32 starts, it looks for DCC32.CFG in the current directory. If the file isn't found there, DCC32 looks in the directory where DCC32.EXE resides.

If DCC32.CFG contains a line that does not start with a slash (/) or a hyphen (-), that line defines a default file name to compile. In that case, starting DCC32 with an empty command line (or with a command line consisting of command-line options only and no file name) will cause it to compile the default file name, instead of displaying a syntax summary.

Here's an example DCC32.CFG file, defining some default directories for include, object, and unit files, and changing the default states of the $F and $S compiler directives:

/IC:\DELPHI\INC;C:\DELPHI\SRC

/OC:\DELPHI\ASM

/UC:\DELPHI\UNITS

/$F+

/$S-

Now, if you type

DCC32 MYSTUFF

at the system prompt, DCC32 acts as if you had typed in the following:

DCC32 /IC:\DELPHI\INC;C:\DELPHI\SRC /OC:\DELPHI\ASM /UC:\DELPHI\UNITS /$F+ /$S- MYSTUFF


See also

Compiler directives


Compiler directives

Section topics	See also

These alphabetically listed topics describe the compiler directives you can use to control the features of the Delphi compiler. Each compiler directive is classified as either a switch, parameter, or conditional compilation directive. Following the list of compiler directives is a brief discussion of how to use the conditional compilation directives. This reference describes how to use conditional constructs and symbols to produce different code from the same source text.

A compiler directive is a comment with a special syntax. Delphi allows compiler directives wherever comments are allowed. A compiler directive starts with a $ as the first character after the opening comment delimiter, immediately followed by a name (one or more letters) that designates the particular directive. You can include comments after the directive and any necessary parameters.

Three types of directives are described in this appendix:

Switch directives turn particular compiler features on or off. For the single-letter versions, you add either + or - immediately after the directive letter. For the long version, you supply the word "on" or "off."

Switch directives are either global or local.

Global directives affect the entire compilation and must appear before the declaration part of the program or the unit being compiled.

Local directives affect only the part of the compilation that extends from the directive until the next occurrence of the same directive. They can appear anywhere.

Switch directives can be grouped in a single compiler directive comment by separating them with commas with no intervening spaces. For example,

  {$B+,R-,S-}

Parameter directives. These directives specify parameters that affect the compilation, such as file names and memory sizes.

Conditional directives. These directives control conditional compilation of parts of the source text, based on user-definable conditional symbols. See "Using conditional compilation directives" for information about using conditional directives.

All directives, except switch directives, must have at least one space between the directive name and the parameters. Here are some examples of compiler directives:

{$B+}

{$STACKCHECKS ON}

{$R- Turn off range checking}

{$I TYPES.INC}

{$M 32768,4096}

{$DEFINE Debug}

{$IFDEF Debug}

{$ENDIF}

You can insert compiler directives directly into your source code. You can also change the default directives for both the command-line compiler (DCC32.EXE) and the IDE (DELPHI32.EXE).

The Project|Options|Compiler dialog box contains many of the compiler directives; any changes you make to the settings there will affect all units whenever their source code is recompiled in subsequent compilations of that project. If you change a compiler switch and compile (make), none of your units will reflect the change. If you Build All, all units (that you have source code for), will be recompiled with the new settings.

When using the command-line compiler, you can specify compiler directives on the command line (for example, DCC32 /$R+ MYPROG), or you can place directives in a configuration file (see "The DCC32.CFG file"). Compiler directives in the source code always override the command-line compiler directives and the IDE project options.

If you are working in the Delphi editor and want a quick way to see what compiler directives are in effect, press Ctrl+O O. Delphi will insert the current settings in the edit window at the top of your file.


Align fields

Section topics	See also

Type	Switch
Syntax	{$A+} or {$A-} {$ALIGN ON} or {$ALIGN OFF}
Default	{$A+} {$ALIGN ON}
Scope	Local
Remarks

The $A directive controls alignment of fields in record types.

In the {$A+} state, fields in record types that are declared without the packed modifier are aligned. In the {$A-} state, fields in record types are never aligned. Record type field alignment is described in the Object Pascal Language Guide.

Regardless of the state of the $A directive, variables and typed constants are always aligned for optimal access. In the {$A+} state, execution will be faster.


Application type

Section topics	See also

Type	Parameter
Syntax	{$APPTYPE GUI} or {$APPTYPE CONSOLE}
Default	{$APPTYPE GUI}
Scope	Global
Remarks

The $APPTYPE directive controls whether to generate a Win32 console or graphical UI application. See also /C compiler option in "/CC".

In the {$APPTYPE GUI} state, the compiler generates a graphical UI application. This is the normal state for a Delphi application.

In the {$APPTYPE CONSOLE} state, the compiler generates a console application. When a console application is started, Windows creates a text-mode console window through which the user can interact with the application. The Input and Output standard text files are automatically associated with the console window in a console application.

The IsConsole Boolean variable declared in the System unit can be used to detect whether a program is running as a console or graphical UI application.

The $APPTYPE directive is meaningful only in a program. It should not be used in a library, unit, or package.


Assert directives

Section topics	See also

Type	Switch
Syntax	{$C+} or {$C-} {$ASSERTIONS ON} or {$ASSERTIONS OFF}
Default	{$C+} {$ASSERTIONS ON}
Scope	Local
Remarks

The $C directive enables or disables the generation of code for assertions in a source file. {$C+} is the default.

Since assertions are not usually used at runtime in shipping versions of a product, compiler directives that disable the generation of code for assertions are provided. {$C-} will disable assertions.


Boolean short-circuit evaluation

Section topics	See also

Type	Switch
Syntax	{$B+} or {$B-} {$BOOLEVAL ON} or {$BOOLEVAL OFF}
Default	{$B-} {$BOOLEVAL OFF}
Scope	Local
Remarks

The $B directive switches between the two different models of code generation for the and and or Boolean operators.

In the {$B+} state, the compiler generates code for complete Boolean expression evaluation. This means that every operand of a Boolean expression built from the and and or operators is guaranteed to be evaluated, even when the result of the entire expression is already known.

In the {$B-} state, the compiler generates code for short-circuit Boolean expression evaluation, which means that evaluation stops as soon as the result of the entire expression becomes evident in left to right order of evaluation.

For further details, see the section "Boolean operators" in the Object Pascal Language Guide.


Debug information

Section topics	See also

Type	Switch
Syntax	{$D+} or {$D-} {$DEBUGINFO ON} or {$DEBUGINFO OFF}
Default	{$D+} {$DEBUGINFO ON}
Scope	Global
Remarks

The $D directive enables or disables the generation of debug information. This information consists of a line-number table for each procedure, which maps object-code addresses into source text line numbers.

For units, the debug information is recorded in the unit file along with the unit's object code. Debug information increases the size of unit file and takes up additional memory when compiling programs that use the unit, but it does not affect the size or speed of the executable program.

When a program or unit is compiled in the {$D+} state, Delphi's integrated debugger lets you single-step and set breakpoints in that module.

The Include debug info (Project|Options|Linker) and Map file (Project|Options|Linker) options produce complete line information for a given module only if you've compiled that module in the {$D+} state.

The $D switch is usually used in conjunction with the $L switch, which enables and disables the generation of local symbol information for debugging. See also "The generate Turbo Debugger debug info (/V) option" and "Symbol cross-reference information."


DEFINE directive

Section topics	See also

Type	Conditional compilation
Syntax	{$DEFINE name}
Remarks

Defines a conditional symbol with the given name. The symbol is recognized for the remainder of the compilation of the current module in which the symbol is declared, or until it appears in an {$UNDEF name} directive. The {$DEFINE name} directive has no effect if name is already defined.


DENYPACKAGEUNIT directive

Section topics	See also

Type	Switch
Syntax	{$DENYPACKAGEUNIT ON} or {$DENYPACKAGEUNIT OFF}
Default	{$DENYPACKAGEUNIT OFF}
Scope	Local
Remarks

The {$DENYPACKAGEUNIT ON} directive prevents the unit in which it appears from being placed in a package.


Description

Section topics	See also

Type	Parameter
Syntax	{{$DESCRIPTION 'text'}
Scope	Global
Remarks

The $D directive inserts the text you specify into the module description entry in the header of an .EXE, .DLL, or .DPL. Traditionally the text is a name, version number, and copyright notice, but you may specify any text of your choosing. For example,

{$D 'My Application version 12.5'}

Limited to 256 bytes of text. The description is usually not visible to end users. To mark you executable files with descriptive text, version and copyright information for the benefit of end users, use version info resources.

Note

The text description must be included in quotes.


DESIGNONLY directive

Section topics	See also

Type	Switch
Syntax	{$DESIGNONLY ON} or {$DESIGNONLY OFF}
Default	{$DESIGNONLY OFF}
Scope	Local
Remarks

The {DESIGNONLY ON} directive causes the package where it occurs to be compiled for installation in the Delphi IDE. For more information, see "Package-specific compiler directives" in the Object Pascal Language Guide.

Place the DESIGNONLY directive only in .DPK files.


ELSE directive

Section topics	See also

Type	Conditional compilation
Syntax	{$ELSE}
Remarks

Switches between compiling and ignoring the source text delimited by the previous {$IFxxx} and the next {$ENDIF}.


ENDIF directive

Section topics	See also

Type	Conditional compilation
Syntax	{$ENDIF}
Remarks

Ends the conditional compilation initiated by the last {$IFxxx} directive.


Extended syntax

Section topics	See also

Type	Switch
Syntax	{$X+} or {$X-} {$EXTENDEDSYNTAX ON} or {$EXTENDEDSYNTAX OFF}
Default	{$X+} {$EXTENDEDSYNTAX ON}
Scope	Global
Remarks

The $X directive enables or disables Delphi's extended syntax:

Function statements. In the {$X+} mode, function calls can be used as procedure calls; that is, the result of a function call can be discarded. Generally, the computations performed by a function are represented through its result, so discarding the result makes little sense. However, in certain cases a function can carry out multiple operations based on its parameters and some of those cases might not produce a useful result. When that happens, the {$X+} extensions allow the function to be treated as a procedure.

Null-terminated strings. A {$X+} compiler directive enables Delphi's support for null-terminated strings by activating the special rules that apply to the built-in PChar type and zero-based character arrays. For more details about null-terminated strings, see "Using null-terminated strings" in the Object Pascal Language Guide.

Note:	The $X directive is provided for backwards compatibility with previous versions of Borland Pascal. You should not use the {$X-} mode when writing Delphi applications.


Hints

Section topics	See also

Type	Switch
Syntax	{$HINTS ON} or {$HINTS OFF}
Default	{$HINTS ON}
Scope	Local
Remarks

The $HINTS directive controls the generation of hint messages by the compiler.

In the {$HINTS ON} state, the compiler issues hint messages when detecting unused variables, unused assignments, for or while loops that never execute, and so on. In the {$HINTS OFF} state, the compiler generates no hint messages.

By placing code between {$HINTS OFF} and {$HINTS ON} directives, you can selectively turns off hints that you don't care about. For example,

{$HINTS OFF}

procedure Test;

var

 I: Integer;

begin

end;

{$HINTS ON}

Because of the $HINTS directives the compiler will not generate an unused variable hint when compiling the procedure above.


Implicit Build

Section topics	See also

Type	Switch
Syntax	{$IMPLICITBUILD ON} or {$IMPLICITBUILD OFF}
Default	{$IMPLICITBUILD ON}
Scope	Global
Remarks

The {$IMPLICITBUILD OFF} directive, intended only for packages, prevents the source file in which it occurs from being implicitly recompiled later. Use {$IMPLICITBUILD OFF} in .DPK files when compiling packages that provide low-level functionality, that change infrequently between builds, or whose source code will not be distributed. Use of {$IMPLICITBUILD OFF} in unit source files is not recommended. See also "The disable implicit compilation (/Z) option."


Imported data

Section topics	See also

Type	Switch
Syntax	{$G+} or {$G-} {$IMPORTEDDATA ON} or {$IMPORTEDDATA OFF}
Default	{$G+} {$IMPORTEDDATA ON}
Scope	Local
Remarks

The {$G-} directive disables creation of imported data references. Using {$G-} increases memory-access efficiency, but prevents a packaged unit where it occurs from referencing variables in other packages.


IFDEF directive

Section topics	See also

Type	Conditional compilation
Syntax	{$IFDEF name}
Remarks

Compiles the source text that follows it if name is defined.


IFNDEF directive

Section topics	See also

Type	Conditional compilation
Syntax	{$IFNDEF name}
Remarks

Compiles the source text that follows it if name is not defined.


IFOPT directive

Section topics	See also

Type	Conditional compilation
Syntax	{$IFOPT switch}
Remarks

Compiles the source text that follows it if switch is currently in the specified state. switch consists of the name of a switch option, followed by a + or a - symbol. For example, the construct

{$IFOPT R+}

 Writeln('Compiled with range-checking');

{$ENDIF}

will compile the Writeln statement if the $R option is currently active.


Image base address

Section topics	See also

Type	Parameter
Syntax	{$IMAGEBASE number}
Default	{$IMAGEBASE $00400000}
Scope	Global

The $IMAGEBASE directive controls the default load address for an application, DLL, or DPL. The number argument must be a 32-bit integer value that specifies image base address. The number argument must be greater than or equal to $00010000, and the lower 16 bits of the argument are ignored and should be zero.

When a module (application or library) is loaded into the address space of a process, Windows will attempt to place the module at its default image base address. If that does not succeed, that is if the given address range is already reserved by another module, the module is relocated to an address determined at runtime by Windows.

There is seldom, if ever, any reason to change the image base address of an application. For a library, however, it is recommended that you use the $IMAGEBASE directive to specify a non-default image base address, since the default image base address of $00400000 will almost certainly never be available. The recommended address range of DLL images is $40000000 to $7FFFFFFF. Addresses in this range are always available to a process in both Windows NT and Windows 95.

When Windows succeeds in loading a DLL (or DPL) at its image base address, the load time is decreased because relocation fixups does not have to be applied. Furthermore, when the given address range is available in multiple processes that use the library, code portions of the DLL's image can be shared among the processes, thus reducing load time and memory consumption.

Note	::

The $IMAGEBASE directive overrides any value supplied with the /K command line compiler directive option.


Include file

Section topics	See also

Type	Parameter
Syntax	{$I filename} {$INCLUDE filename}
Scope	Local
Remarks

The $I parameter directive instructs the compiler to include the named file in the compilation. In effect, the file is inserted in the compiled text right after the {$I filename} directive. The default extension for filename is .PAS. If filename does not specify a directory path, then, in addition to searching for the file in the same directory as the current module, Delphi searches in the directories specified in the Search path input box on the Directories/Conditionals page of the Project|Options dialog box (or in the directories specified in a /I option on the DCC32 command line).

There is one restriction to the use of include files: An include file can't be specified in the middle of a statement part. In fact, all statements between the begin and end of a statement part must exist in the same source file.


Input/output checking

Section topics	See also

Type	Switch
Syntax	{$I+} or {$I-} {$IOCHECKS ON} or {$IOCHECKS OFF}
Default	{$I+} {$IOCHECKS ON}
Scope	Local
Remarks

The $I switch directive enables or disables the automatic code generation that checks the result of a call to an I/O procedure. I/O procedures are described in the Object Pascal Language Guide. If an I/O procedure returns a nonzero I/O result when this switch is on, an EInOutError exception is raised (or the program is terminated if exception handling is not enabled). When this switch is off, you must check for I/O errors by calling IOResult.


Link object file

Section topics	See also

Type	Parameter
Syntax	{$L filename} {$LINK filename}
Scope	Local
Remarks

The $L parameter instructs the compiler to link the named file with the program or unit being compiled. The $L directive is used to link with code written in other languages for procedures and functions declared to be external. The named file must be an Intel relocatable object file (.OBJ file). The default extension for filename is .OBJ. If filename does not specify a directory path, then, in addition to searching for the file in the same directory as the current module, Delphi searches in the directories specified in the Search path input box on the Directories/Conditionals page of the Project|Options dialog box (or in the directories specified in a /O option on the DCC32 command line). For further details about linking with assembly language, see online Help.


Local symbol information

Section topics	See also

Type	Switch
Syntax	{$L+} or {$L-} {$LOCALSYMBOLS ON} or {$LOCALSYMBOLS OFF}
Default	{$L+} {$LOCALSYMBOLS ON}
Scope	Global
Remarks

The $L switch directive enables or disables the generation of local symbol information. Local symbol information consists of the names and types of all local variables and constants in a module, that is, the symbols in the module's implementation part and the symbols within the module's procedures and functions.

For units, the local symbol information is recorded in the unit file along with the unit's object code. Local symbol information increases the size of unit files and takes up additional memory when compiling programs that use the unit, but it does not affect the size or speed of the executable program.

When a program or unit is compiled in the {$L+} state, Delphi's integrated debugger lets you examine and modify the module's local variables. Furthermore, calls to the module's procedures and functions can be examined via the View|Call Stack.

The Include TDW debug info and Map file options on the Linker page of the Project|Options dialog box produce local symbol information for a given module only if that module was compiled in the {$L+} state.

The $L switch is usually used in conjunction with the $D switch, which enables and disables the generation of line-number tables for debugging. The $L directive is ignored if the compiler is in the {$D-} state.


Long strings

Section topics	See also

Type	Switch
Syntax	{$H+} or {$H-} {$LONGSTRINGS ON} or {$LONGSTRINGS OFF}
Default	{$H+} {$LONGSTRINGS ON}
Scope	Local
Remarks

The $H directive controls the meaning of the reserved word string when used alone in a type declaration. The generic type string can represent either a long, dynamically-allocated string (the fundamental type AnsiString) or a short, statically-allocated string (the fundamental type ShortString).

By default {$H+}, Delphi defines the generic string type to be the long AnsiString. All components in the Visual Component Library are compiled in this state. If you write components, they should also use long strings, as should any code that receives data from VCL string-type properties.

The {$H-} state is mostly useful for using code from versions of Object Pascal that used short strings by default. You can locally override the meaning of string-type definitions to ensure generation of short strings. You can also change declarations of short string types to string[255] or ShortString, which are unambiguous and independent of the $H setting.


Memory allocation sizes

Section topics	See also

Type	Parameter
Syntax	{$M minstacksize,maxstacksize} {$MINSTACKSIZE number} {$MAXSTACKSIZE number}
Default	{$M 16384,1048576}
Scope	Global
Remarks

The $M directive specifies an application's stack allocation parameters. minstacksize must be an integer number between 1024 and 2147483647 that specifies the minimum size of an application's stack, and maxstacksize must be an integer number between minstacksize and 2147483647 that specifies the maximum size of an application's stack.

If there is not enough memory available to satisfy an application's minimum stack requirement, Windows will report an error upon attempting to start the application.

An application's stack is never allowed to grow larger than the maximum stack size. Any attempt to grow the stack beyond the maximum stack size causes an EStackOverflow exception to be raised.

The $MINSTACKSIZE and $MAXSTACKSIZE directives allow the minimum and maximum stack sizes to be specified separately.

The memory allocation directives are meaningful only in a program. They should not be used in a library or a unit.


Minimum enumeration size

Section topics	See also

Type	Parameter
Syntax	{$Z1} or {$Z2} or {$Z4} {$MINENUMSIZE 1} or {$MINENUMSIZE 2} or {$MINENUMSIZE 4}
Default	{$Z1} {$MINENUMSIZE 1}
Scope	Local

The $Z directive controls the minimum storage size of enumerated types.

An enumerated type is stored as an unsigned byte if the enumeration has no more than 256 values, and if the type was declared in the {$Z1} state (the default). If an enumerated type has more than 256 values, or if the type was declared in the {$Z2} state, it is stored as an unsigned word. Finally, if an enumerated type is declared in the {$Z4} state, it is stored as an unsigned double-word.

The {$Z2} and {$Z4} states are useful for interfacing with C and C++ libraries, which usually represent enumerated types as words or double-words.

Note:	For backwards compatibility with earlier versions of Delphi and Borland Pascal, the directives {$Z-} and {$Z+} are also supported. They correspond to {$Z1} and {$Z4}, respectively.


Open String Parameters

Section topics	See also

Type	Switch
Syntax	{$P+} or {$P-} {$OPENSTRINGS ON} or {$OPENSTRINGS OFF}
Default	{$P+} {$OPENSTRINGS ON}
Scope	Local
Remarks

The $P directive is meaningful only for code compiled in the {$H-} state, and is provided for backwards compatibility with earlier versions of Delphi and Borland Pascal. $P controls the meaning of variable parameters declared using the string keyword in the {$H-} state. In the {$P-} state, variable parameters declared using the string keyword are normal variable parameters, but in the {$P+} state, they are open string parameters. Regardless of the setting of the $P directive, the OpenString identifier can always be used to declare open string parameters. For more information about open parameters, see the Object Pascal Language Reference.


Optimization

Section topics	See also

Type	Switch
Syntax	{$O+} or {$O-} {$OPTIMIZATION ON} or {$OPTIMIZATION OFF}
Default	{$O+} {$OPTIMIZATION ON}
Scope	Local

The $O directive controls code optimization. In the {$O+} state, the compiler performs a number of code optimizations, such as placing variables in CPU registers, eliminating common subexpressions, and generating induction variables. In the
{$O-} state, all such optimizations are disabled.

Other than for certain debugging situations, you should never have a need to turn optimizations off. All optimizations performed by Delphi's Object Pascal compiler are guaranteed not to alter the meaning of a program. In other words, Delphi performs no "unsafe" optimizations that require special awareness by the programmer.


Overflow checking

Section topics	See also

Type	Switch
Syntax	{$Q+} or {$Q-} {$OVERFLOWCHECKS ON} or {$OVERFLOWCHECKS OFF}
Default	{$Q-} {$OVERFLOWCHECKS OFF}
Scope	Local
Remarks

The $Q directive controls the generation of overflow checking code. In the {$Q+} state, certain integer arithmetic operations (+, -, *, Abs, Sqr, Succ, Pred, Inc, and Dec) are checked for overflow. The code for each of these integer arithmetic operations is followed by additional code that verifies that the result is within the supported range. If an overflow check fails, an EIntOverflow exception is raised (or the program is terminated if exception handling is not enabled).

The $Q switch is usually used in conjunction with the $R switch, which enables and disables the generation of range-checking code. Enabling overflow checking slows down your program and makes it somewhat larger, so use {$Q+} only for debugging.


Pentium-safe FDIV operations

Section topics	See also

Type	Switch
Syntax	{$U+} or {$U-} {$SAFEDIVIDE ON} or {$SAFEDIVIDE OFF}
Default	{$U-}
Scope	Local

The $U directive controls generation of floating-point code that guards against the flawed FDIV instruction exhibited by certain early Pentium processors. Windows 95, Windows NT 3.51 and 4.0 contain code which corrects the Pentium FDIV bug system-wide.

In the {$U+} state, all floating-point divisions are performed using a runtime library routine. The first time the floating-point division routine is invoked, it checks whether the processor's FDIV instruction works correctly, and updates the TestFDIV variable (declared in the System unit) accordingly. For subsequent floating-point divide operations, the value stored in TestFDIV is used to determine what action to take.

Value	Meaning
-1	FDIV instruction has been tested and found to be flawed.
0	FDIV instruction has not yet been tested.
1	FDIV instruction has been tested and found to be correct.

For processors that do not exhibit the FDIV flaw, {$U+} results in only a slight performance degradation. For a flawed Pentium processor, floating-point divide operations may take up to three times longer in the {$U+} state, but they will always produce correct results.

In the {$U-} state, floating-point divide operations are performed using in-line FDIV instructions. This results in optimum speed and code size, but may produce incorrect results on flawed Pentium processors. You should use the {$U-} state only in cases where you are certain that the code is not running on a flawed Pentium processor.


Range checking

Section topics	See also

Type	Switch
Syntax	{$R+} or {$R-} {$RANGECHECKS ON} or {$RANGECHECKS OFF}
Default	{$R-} {$RANGECHECKS OFF}
Scope	Local
Remarks

The $R directive enables or disables the generation of range-checking code. In the {$R+} state, all array and string-indexing expressions are verified as being within the defined bounds, and all assignments to scalar and subrange variables are checked to be within range. If a range check fails, an ERangeError exception is raised (or the program is terminated if exception handling is not enabled).

Enabling range checking slows down your program and makes it somewhat larger, so use the {$R+} only for debugging.

Note:	Long strings are not range checked.


Resource file

Section topics	See also

Type	Parameter
Syntax	{$R Filename} {$RESOURCE filename}
Scope	Local
Remarks

The $R directive specifies the name of a resource file to be included in an application or library. The named file must be a Windows resource file and the default extension for filename is .RES.

When a {$R filename} directive is used in a unit, the specified file name is simply recorded in the resulting unit file. No checks are made at that point to ensure that the filename is correct and that it specifies an existing file.

When an application or library is linked (after compiling the program or library source file), the resource files specified in all used units as well as in the program or library itself are processed, and each resource in each resource file is copied to the executable being produced. During the resource processing phase, Delphi's linker searches for .RES files in the same directory as the module containing the $R directive, and in the directories specified in the Search path input box on the Directories/Conditionals page of the Project|Options dialog box (or in the directories specified in a /R option on the DCC32 command line).


RUNONLY directive

Section topics	See also

Type	Switch
Syntax	{$RUNONLY ON} or {$RUNONLY OFF}
Default	{$RUNONLY OFF}
Scope	Local
Remarks

The {$RUNONLY ON} directive causes the package where it occurs to be compiled as runtime only. Packages compiled with {$RUNONLY ON} cannot be installed as design-time packages in the Delphi IDE.

Place the RUNONLY directive only in .DPK files. For more information, see the Object Pascal Language Guide.


Runtime type information

Section topics	See also

Type	Switch
Syntax	{$M+} or {$M-} {$TYPEINFO ON} or {$TYPEINFO OFF}
Default	{$M-} {$TYPEINFO OFF}
Scope	Local

The $M switch directive controls generation of runtime type information (RTTI). When a class is declared in the {$M+} state, or is derived from a class that was declared in the {$M+} state, the compiler generates run-time type information for fields, methods, and properties that are declared in a published section. If a class is declared in the {$M-} state, and is not derived from a class that was declared in the {$M+} state, published sections are not allowed in the class.

Note:	The TPersistent class defined in the Classes unit of the Delphi Visual Class Library is declared in the {$M+} state, so any class derived from TPersistent will have RTTI generated for its published sections. The Delphi Visual Component Library uses the run-time type information generated for published sections to access the values of a component's properties when saving a loading form files. Furthermore, the Delphi IDE uses a component's run-time type information to determine the list of properties to show in the Object Inspector.

There is seldom, if ever, any need for an application to directly use the $M compiler switch.


Symbol cross-reference information

Section topics	See also

Type	Switch
Syntax	{$Y+} or {$Y-} {$REFERENCEINFO ON} or {$REFERENCEINFO OFF}
Default	{$Y+} {$REFERENCEINFO ON}
Scope	Global
Remarks

The $Y directive enables or disables generation of symbol reference information. This information consists of tables that provide the line numbers of all declarations of and references to symbols in a module.

For units, the symbol reference information is recorded in the .DCU file along with the unit's object code. Symbol reference information increases the size of the .DCU files, but it does not affect the size or speed of the executable program.

When a program or unit is compiled in the {$Y+} state, Delphi's integrated browser can display symbol definition and reference information for that module.

The $Y switch is usually used in conjunction with the $D and $L switches, which control generation of debug information and local symbol information. The $Y directive has no effect unless both $D and $L are enabled.

Note:	Generating symbol cross-reference information can considerably slow the compile/link cycle, so you should leave this turned off except when you need to use the Object Browser.


Type-checked pointers

Section topics	See also

Type	Switch
Syntax	{$T+} or {$T-} {$TYPEDADDRESS ON} or {$TYPEDADDRESS OFF}
Default	{$T-} {$TYPEDADDRESS OFF}
Scope	Global
Remarks

The $T directive controls the types of pointer values generated by the @ operator. In the {$T-} state, the result type of the @ operator is always an untyped pointer that is compatible with all other pointer types. When @ is applied to a variable reference in the {$T+} state, the type of the result is ^T, where T is compatible only with other pointers to the type of the variable.


UNDEF directive

Section topics	See also

Type	Conditional compilation
Syntax	{$UNDEF name}
Remarks

Undefines a previously defined conditional symbol. The symbol is forgotten for the remainder of the compilation of the current source file or until it reappears in a {$DEFINE name} directive. The {$UNDEF name} directive has no effect if name is already undefined.

Conditional symbols defined with command-line switch or IDE Project|Options are reinstated at the start of compilation of each unit source file. Conversely, conditional symbols defined in a unit source file are forgotten when the compiler starts on another unit.


Var-string checking

Section topics	See also

Type	Switch
Syntax	{$V+} or {$V-} {$VARSTRINGCHECKS ON} or {$VARSTRINGCHECKS OFF}
Default	{$V+} {$VARSTRINGCHECKS ON}
Scope	Local
Remarks

The $V directive is meaningful only for code that uses short strings (see Long strings compiler directive), and is provided for backwards compatibility with earlier versions of Delphi and Borland Pascal.

The $V directive controls type checking on short strings passed as variable parameters. In the {$V+} state, strict type checking is performed, requiring the formal and actual parameters to be of identical string types. In the {$V-} (relaxed) state, any short string type variable is allowed as an actual parameter, even if the declared maximum length is not the same as that of the formal parameter.


Warnings

Section topics	See also

Type	Switch
Syntax	{$WARNINGS ON} or {$WARNINGS OFF}
Default	{$WARNINGS ON}
Scope	Local
Remarks

The $WARNINGS directive controls the generation of compiler warnings.

In the {$WARNINGS ON} state, the compiler issues warning messages when detecting uninitialized variables, missing function results, construction of abstract objects, and so on. In the {$WARNINGS OFF} state, the compiler generates no warning messages.

By placing code between {$WARNINGS OFF} and {$WARNINGS ON} directives, you can selectively turns off warnings that you don't care about.


Weak packaging

Section topics	See also

Type	Switch
Syntax	{$WEAKPACKAGEUNIT ON} or {$WEAKPACKAGEUNIT OFF}
Default	{$WEAKPACKAGEUNIT OFF}
Scope	Local
Remarks

The $WEAKPACKAGEUNIT directive affects the way a .DCU file is stored in a package's .DCP and .DPL files. If {$WEAKPACKAGEUNIT ON} appears in a unit file, the compiler omits the unit from DPLs when possible, and creates a non-packaged local copy of the unit when it is required by another application or package. A unit compiled with this directive is said to be "weakly packaged."

For example, suppose a package called PACK contains only one unit, UNIT1. Suppose UNIT1 does not use any further units, but it makes calls to RARE.DLL. If the {$WEAKPACKAGEUNIT ON} directive is inserted in UNIT1.PAS before compiling, UNIT1 will not be included in PACK.DPL; copies of RARE.DLL will not have to be distributed with PACK. However, UNIT1 will still be included in PACK.DCP. If UNIT1 is referenced by another package or application that uses PACK, it will be copied from PACK.DCP and compiled directly into the project.

Now suppose a second unit, UNIT2, is added to PACK. Suppose that UNIT2 uses UNIT1. This time, even if PACK is compiled with {$WEAKPACKAGEUNIT ON} in UNIT1.PAS, the compiler will include UNIT1 in PACK.DPL. But other packages or applications that reference UNIT1 will use the (non-packaged) copy taken from PACK.DCP.

Note:	Unit files containing the {$WEAKPACKAGEUNIT ON} directive must not have global variables, initialization sections, or finalization sections.

The $WEAKPACKAGEUNIT directive is an advanced feature intended for developers who distribute their DPLs to other Delphi programmers. It can help to avoid distribution of infrequently used DLLs, and to eliminate conflicts among packages that may depend on the same external library.

For example, Delphi's PenWin unit references PENWIN.DLL. Most projects don't use PenWin, and most computers don't have PENWIN.DLL installed on them. For this reason, the PenWin unit is weakly packaged in VCL30 (which encapsulates many commonly used Delphi components). When you compile a project that uses PenWin and the VCL30 package, PenWin is copied from VCL30.DCP and bound directly into your project; the resulting executable is statically linked to PENWIN.DLL.

If PenWin were not weakly packaged, two problems would arise. First, VCL30 itself would be statically linked to PENWIN.DLL, and so could not be loaded on any computer which didn't have PENWIN.DLL installed. Second, if someone tried to create a package that contained PenWin, a compiler error would result because the PenWin unit would be contained in both VCL30 and the new package. Thus, without weak packaging, PenWin could not be included in standard distributions of VCL30.


Windows stack frames

Section topics	See also

Type	Switch
Syntax	{$W+} or {$W-} {$STACKFRAMES ON} or {$STACKFRAMES OFF}
Default	{$W-} {$STACKFRAMES OFF}
Scope	Local
Remarks

The $W directive controls the generation of stack frames for procedures and functions. In the {$W+} state, stack frames are always generated for procedures and function, even when they're not needed. In the {$W-} state, stack frames are only generated when they're required, as determined by the routine's use of local variables.

Some debugging tools require stack frames to be generated for all procedures and functions, but other than that you should never have a need to use the {$W+} state.


Writeable typed constants

Section topics	See also

Type	Switch
Syntax	{$J+} or {$J-} {$WRITEABLECONST ON} or {$WRITEABLECONST OFF}
Default	{$J+} {$WRITEABLECONST ON}
Scope	Local

The $J directive controls whether typed constants can be modified or not. In the {$J+} state, typed constants can be modified, and are in essence initialized variables. In the {$J-} state, typed constants are truly constant, and any attempt to modify a typed constant causes the compiler to report an error.

In previous versions of Delphi and Borland Pascal, typed constants were always writeable, corresponding to the {$J+} state. Old source code that uses writeable typed constants must be compiled in the {$J+} state, but for new applications it is recommended that you use initialized variables and compile your code in the {$J-} state.


Using conditional compilation directives

Section topics	See also

Two basic conditional compilation constructs closely resemble Pascal's if statement. The first construct

{$IFxxx}

  ...

{$ENDIF}

causes the source text between {$IFxxx} and {$ENDIF} to be compiled only if the condition specified in {$IFxxx} is True. If the condition is False, the source text between the two directives is ignored.

The second conditional compilation construct:

{$IFxxx}

  ...

{$ELSE}

  ...

{$ENDIF}

causes either the source text between {$IFxxx} and {$ELSE} or the source text between {$ELSE} and {$ENDIF} to be compiled, depending on the condition specified by the {$IFxxx}.

Here are some examples of conditional compilation constructs:

{$IFDEF Debug}

 Writeln('X = ', X);

{$ENDIF}

{$IFDEF WIN32}

 P := SmallPointToPoint(Message.Pos);

{$ELSE}

 P := Message.Pos;

{$ENDIF}

You can nest conditional compilation constructs up to 16 levels deep. For every {$IFxxx}, the corresponding {$ENDIF} must be found within the same source file, which means there must be an equal number of {$IFxxx}'s and {$ENDIF}'s in every source file.


Conditional symbols

Section topics	See also

Conditional compilation is based on the evaluation of conditional symbols. Conditional symbols are defined and undefined using the directives

{$DEFINE name}

{$UNDEF name}

You can also use the /D switch in the command-line compiler to define a symbol (or add the symbol to the Conditional Defines input box on the Directories/Conditionals page of the Project|Options dialog box in the IDE).

Conditional symbols are best compared to Boolean variables: They are either True (defined) or False (undefined). The {$DEFINE} directive sets a given symbol to True, and the {$UNDEF} directive sets it to False.

Conditional symbols follow the same rules as Pascal identifiers: They must start with a letter, followed by any combination of letters, digits, and underscores. They can be of any length, but only the first 255 characters are significant.

Conditional symbols and Pascal identifiers have no correlation whatsoever. Conditional symbols cannot be referenced in the actual program and the program's identifiers cannot be referenced in conditional directives. For example, the construct

const

 Debug = True;

begin

 {$IFDEF Debug}

 Writeln('Debug is on');

 {$ENDIF}

end;

will not compile the Writeln statement. Likewise, the construct

{$DEFINE Debug}

begin

 if Debug then

   Writeln('Debug is on');

end;

will result in an unknown identifier error in the if statement.

Delphi defines the following standard conditional symbols

VER100	Always defined, indicating that this is version 10.0 of the Object Pascal compiler. Each version has corresponding predefined symbols; for example, version 10.1 would have VER101 defined, version 10.5 would have VER105 defined, and so on.
WIN32	Indicates that the operating environment is the Win32 API.
CPU386	Indicates that the CPU is an Intel 386 or better.
CONSOLE	Defined if an application is being compiled as a console application.

Other conditional symbols can be defined before a compilation by using the Conditional Defines input box, or the /D command-line option if you are using the command-line compiler.

Note:	Source code conditional defines are evaluated only when the source code is recompiled. As mentioned earlier, changing a conditional define and recompiling the project (make), will not cause units to be recompiled from source, and therefore the units will not reflect the change in conditional defines. "Build All" is the only way to ensure everything in a project reflects changes to conditional defines.


See also

The command-line compiler


Making effective use of the development environment

Section topics 	See also

Delphi provides a development environment with tools and components that are especially useful to database application developers. Some of these are generic to the product, such as the Object Repository and data modules, but others are specific to database development, such as the Database/SQL Explorer, and client dataset components. The following topics highlight these Delphi features and tell you where to go for more information about them.

These topics also introduce Delphi's database architecture. It provides a general diagram of Delphi's open database architecture, and more specific diagrams of the structures of each type of database application you can create with Delphi. It also offers diagrams that relate Delphi database components as they are used in single-tiered, two-tiered, and multi-tiered applications.

The database features and tools in Delphi form a tightly-integrated suite designed to ease the design, implementation, deployment, and maintenance of database applications in a variety of computing environments.

Some Delphi databases features and tools, such as the SQL Explorer, SQL Monitor, client datasets, remote data modules, and provider components, are specific to the Delphi Client/Server Suite (the Database Explorer replaces the SQL Explorer in Delphi Professional and Delphi Standard editions). Other tools, such as standard database components, the BDE, and the standalone Database Desktop (DBD) are provided in all editions of Delphi.

In general, the Delphi Client/Server Suite provides the tightest integration of tools and the power necessary for building, testing, and deploying two-tiered and multi-tiered applications that work with SQL databases on remote servers, and local and networked Paradox®, dBASE®, MS Access, and FoxPro databases. With third-party ODBC drivers you can also access local and remote ODBC-compliant databases.

Delphi Professional enables you to build and test client applications for local and networked Paradox, dBASE, Access, and FoxPro databases. With third-party ODBC drivers you can also access local and remote ODBC-compliant databases.

Delphi Standard enables building and testing of client applications for local and networked Paradox, dBASE, Access, and FoxPro databases.


Browsing and designing with the SQL Explorer

Section topics 	See also

In the Delphi Client/Server and Enterprise Suites you browse databases and populate tables with data using the SQL Explorer from the IDE. In Delphi Professional you use the Database Explorer. Both versions of the Explorer enable you to:

Examine existing database tables and structures. The SQL Explorer enables you to examine and query remote SQL databases.

Create SQL objects on remote database servers, such as stored procedures with the SQL Explorer. You can also view the reconstructed text of SQL objects on the server.

Run SQL scripts in the SQL Explorer.

Populate tables with data.

Create extended field attribute sets in a Data Dictionary for later retrieval and reuse. Extended field attributes describe how values in a field are formatted and displayed.

Associate extended field attributes with fields in your application.

Create and manage BDE aliases, used by your application to connect to databases.

To learn more about the SQL Explorer, and the Database Explorer see their respective online Help files.


Using the Data Dictionary

Section topics

The Data Dictionary provides a customizable storage area, independent of your applications, where you can create extended field attribute sets that describe the content and appearance of data.

For example, if you frequently develop financial applications, you may create a number of specialized field attribute sets describing different display formats for currency. When you create datasets for your application at design time, rather than using the Object Inspector to set the currency fields in each dataset by hand, you can associate those fields with extended fields attribute set in the Data Dictionary. Using the Data Dictionary also ensures a consistent data appearance within and across the applications you create.

In a client/server environment the Data Dictionary can reside on a remote server for additional sharing of information.

To learn how to create extended field attribute sets from the Fields editor at design time, and how to associate them with fields throughout the datasets in your application, see "Working with field components." To learn more about creating a Data Dictionary and extended field attributes with the SQL and Database Explorers, see their respective online Help files.


Creating multi-tiered database applications

Section topics 	See also

New Delphi database components enable you to create multi-tiered database applications.

TClientDataset enables you to create a database-engine independent dataset that can retrieve data from local data sources, files, and remote application servers. You can use TClientDataset to create single-tiered database applications that do not use the Borland Database Engine (BDE), and you can use TClientDataSet components in client applications as part of a multi-tiered database application. For more information about TClientDataSet, see "Creating and using a client dataset."

TMIDASConnection enables you to create a client application that connects to a remote application server using DCOM, TCP/IP sockets, or OLEnterprise. For more information about using TMIDASConnection, see "Creating a multi-tiered application."

TProvider enables you to create a dual interface between a client application and the application server (where TProvider components reside), so that client datasets in a client application can communicate through the provider or directly with datasets on the application server.


Designing a database interface with data-aware controls

Section topics 	See also

By placing database access components in data modules, you can now develop forms in your database applications that are concerned only with a consistent user interface. With the extended functionality of TDBGrid controls, flexible TDBLookupListBox and TDBLookupComboBox controls, and a TDBCtrlGrid control that provides a new multi-field, multi-record view of data, your application's user interface can be more compelling and effective.

By storing links to well-designed forms and data modules in the Object Repository, you and other developers can build on existing foundations rather than starting over from scratch for each new project. Sharing forms and modules also makes it possible for you to develop corporate standards for database access and application interfaces.

For more information about working with data controls, see "Using data controls." For information about using grids, see "Working with grids."


Using the Database Desktop with Paradox and dBASE files

Section topics 	See also

You can use the Database Desktop (DBD) to browse and modify existing Paradox and dBASE tables or create and populate new ones, create indexes, define referential integrity, and create database-level validation and business rules for them. You can browse and create BDE aliases as well.

The DBD is a standalone utility that runs outside the Delphi IDE. For more information about the DBD, see its online Help.


Using data modules and remote data modules

Section topics 	See also

Data modules radically simplify data access development in your applications. Data modules offer you a centralized design-time container for all your data access components that enables you to modularize your code and separate the database access logic and business rules in your applications from the user interface logic in the application's forms.

Once you define your datasets and their fields in a data module, all forms that use the module have consistent access to datasets and fields without requiring you recreate them on every form each time you need them. In fact, data modules can and should be stored in the Object Repository for shared use among developers and applications.

New in Delphi 3.0, the remote data module enables you to create ActiveX data modules that are basis for creating OLE-enabled automation objects that can function as application servers in a multi-tiered, client/server database application.

To learn more about data modules, see the User's Guide. For more information about creating an application server for a multi-tiered database application using remote data modules, see "Creating a multi-tiered application."


Creating and using component templates

Section topics 	See also

Delphi 3.0 introduces component templates. Component templates enable you to set up one or more grouped components on a form, set their properties, write event handlers for them, and then store the resulting component group on the component palette for later reuse. Component templates offer a convenient and fast way for you to create custom interface layouts and designs with default behaviors and implement them consistently across all your corporate applications.

For more information about creating and using component templates, see the User's Guide.


Using the Object Repository

Section topics 	See also

The Object Repository stores links to data modules, forms, and projects for reuse and reference. When you create a new application, you can:

Copy an existing data module, form, or project, ensuring that your copy is completely independent of the repository.

Inherit an existing data module, form, or project, ensuring that changes to the linked module, form, or project in the repository are replicated to your application when you recompile.

Use an existing data module, form, or project, ensuring that changes you make to the module, form, or project are available for use in other applications.

The Object Repository supports team development practices. It uses a referencing mechanism to data modules, forms, and projects where they exist on a network server or shared machine. Every developer in your organization can save objects to a shared location, and then set Delphi's Object Repository reference to point to that location.

The Object Repository is also customizable through the Tools|Repository menu in the IDE.

For general information about using the Object Repository for all your Delphi programming, see the User's Guide.


Overview of Delphi's database architecture

Section topics 	See also

Delphi uses a new, open database architecture that lets you create database applications using any database engine available to you. It even enables you to bypass the Borland Database Engine (BDE), either by using a new, built-in client dataset component, or by deriving your own custom dataset components from the generic, database independent TDataSet component that underlies all other Delphi dataset components with which you may be more familiar. Depending on your edition of Delphi, you can create:

Single-tiered database applications that work through the BDE to access local Paradox and dBASE files.

Single-tiered database applications that read data directly from and write data directly to flat data files.

Two-tiered, client/server client applications that interact with a local or remote database server.

Multi-tiered, partitioned client/server applications, where the client application provides a user interface, and interacts with an application server (sometimes called a remote data broker) that usually resides on another machine and, in turn, interacts with a remote database server.

Each of these development models make use of Delphi's new open dataset architecture. The following illustration offers an overview of this open dataset architecture:

Delphi's open dataset architecture

As the previous figure illustrates, Delphi offers several different ways to construct a database application. As it always has, Delphi continues to provide many ready-made, BDE-enabled database components that enable you to create complex and advanced database applications quickly and easily. For developers who want to create thin, single-tiered database applications or who want to develop complex, multi-tiered database solutions that optionally use or bypass the BDE, Delphi Client/Server Suite provides a new TClientDataSet component. Finally, developers who want to bypass the BDE altogether in order to use third-party or proprietary database engines can derive their own custom dataset components from TDataSet.

The following topics provide closer overviews of each of these models, and describes the Delphi elements common to the user interface for each type of database application you build.


Understanding the user interface

Section topics 	See also

Delphi provides a series of data-aware controls to provide a user interface (UI) to data in all types of Delphi database applications. Data-aware controls display data from fields in a database record, and can permit users to edit that data and post changes back to the database. Data-aware controls are placed on Delphi forms so they are visible and accessible to users.

Most of these controls are similar in appearance and function to standard Windows controls that you place on forms. For example, the TDBEdit control is a data-aware version of the standard TEdit control which enables users to see and edit a text string. Other data-aware controls, such as TDBNavigator, and TDBGrid have an appearance and functionality that is particular to the needs of a database. For example, the navigator control does not display or edit data, but it enables the user to scroll through records, enter edit or insert mode, delete records, post changes to the database, and even request a fresh view of data. TDBGrid can display some or all fields for a number of records at once in a tabular, spreadsheet-like format table summarizes the data-aware visual controls that appear on the Data Controls page:

Component	Purpose
TDBGrid	Display and edit dataset records in tabular format. See "Working with grids."
TDBNavigator	Cursor through dataset records; enable Edit and Insert states; post new or modified records; cancel edit mode; refresh data display. See "Using data controls."
TDBText	Display a field as a label.
TDBEdit	Display and edit a field in an edit box.
TDBMemo	Display and edit multi-line or blob text in a scrollable, multi-line edit box.
TDBImage	Display and edit a graphics image or binary blob data.
TDBListBox	Display a list of choices for entry in a field.
TDBComboBox	Display an edit box and drop-down list of choices for edit and entry in a field.
TDBCheckBox	Display and set a Boolean field condition in a check box.
TDBRadioGroup	Display and set exclusive choices for a field in a radio button group.
TDBLookupListBox	Display a list of choices derived from a field in another dataset for entry into a field.
TDBLookupComboBox	Display an edit box and drop-down list of choices derived from a field in another dataset for edit and entry in a field.
TDBRichEdit	Displays and edit formatted multi-line or blob text in a scrollable, multi-line edit box.
TDBCtrlGrid	(Delphi Client/Server Suite and Professional only). Display and edit records in a tabular grid, where each cell in the grid contains a repeating set of data-aware components and one record. See "Working with grids."

Data-aware controls get data from and send data to a data source component, TDataSource. A data source component acts as a conduit between the user interface, and a dataset component which represents a database table, query, or stored procedure. Several data-aware controls on a form can share a single data source, in which case the display in each control is synchronized so that as the user scrolls through records, the corresponding value in the fields for the current record is displayed in each control. An application's data source components usually reside in a data module, separate from the data-aware controls on forms.

Each data source component in an application connects to a single dataset component. The dataset component is always derived from TDataSet. If you build a traditional, two-tiered client that uses the BDE, then your application uses TQuery, TStoredProc, and TTable components, all of which are descendants of TDataSet. If you build a multi-tiered, ActiveX-enabled database application, then the client portion of the application can use the new TClientDataSet component, which is a dataset representation independent of a particular database engine. Finally, you can derive your own custom dataset descendants to use to interact with your own or other third-party databases. Regardless of the type of dataset component you use, you construct your UI in the same manner. The following figure illustrates the relationship of the components that provide a UI in your database applications to the datasets from which they get data and to which they send updates:

The following table summarizes all the database components that appear on the Data Access page, including the dataset components, and describes where in this guide you can get more information about them:

Component	Purpose
TBatchMove	Moves data from one table to another en masse. For more information about TBatchMove, see "Copying data and table structures."
TClientDataSet	Encapsulates a dataset that is independent of the BDE that can be used in single-tiered applications, and in multi-tiered applications on the client. Available only in Delphi Client/Server Suite. For more information about TClientDataSet, see "Creating and using a client dataset."
TDatabase	Encapsulates a client/server connection to a single database in one session. For more information about TDatabase, see "Connecting to databases."
TDataSource	Acts as a conduit between other data access components and data-aware visual controls. For more information about TDataSource, see "Working with data sources."
TProvider	Provides an ActiveX-enabled dual interface that serves as a communication channel between an application server and client applications in a multi-tiered application. Available only in Delphi Client/Server Suite. For more information about TProvider, see "Creating a multi-tiered application."
TQuery	Represents a dataset that retrieves a subset of columns and records from one or more local or remote database tables based on an SQL query. For more information about TQuery, see "Working with queries."
TMIDASConnection	Connects a client application to an application server in a multi-tiered application. Available only in the Delphi Client/Server Suite. For more information about TMIDASConnection, see "Creating a multi-tiered application."
TSession	Represents a single session in a multi-threaded database application. Each session can have multiple database connections as long as each thread associated with a particular database has its own session. For more information about TSession, see "Managing database sessions."
TStoredProc	Represents a dataset that retrieves one or more records from a database table based on a stored procedure defined for a database server. For more information about TStoredProc, see "Working with stored procedures."
TTable	Represents a dataset that retrieves all columns and records from a database table. For more information about TTable, see "Working with tables."
TUpdateSQL	Represents SQL INSERT, UPDATE, and DELETE statements that can be used to update the read-only result sets of some queries. For more information about TUpdateSQL, see "Working with cached updates."

In addition to these data access components and data-aware controls, Delphi Client/Server Suite includes a DecisionCube page on the Component palette. It contains six components that enable you to perform data analysis and cross-tabulations on data. For more information about using the DecisionCube components, see Decision Cube components and Using decision support components.


Understanding the architecture of a single-tiered BDE application

Section topics 	See also

In a single-tiered database model that uses the BDE to access Paradox and dBASE, a single application comprises the user interface and incorporates the BDE. Previous versions of Delphi have always supported coding this type of single-tiered database application. The following figure illustrates the architectural model for a single-tiered application that uses the BDE:

Single-tiered, BDE-enabled database application architecture

In addition to this single-tiered model, Delphi now supports a new single-tiered model that does not rely on incorporating the BDE into your application. The new, single-tiered model is discussed fully in the following topic.


Understanding the architecture of the new, single-tiered application

Section topics 	See also

In Delphi's new, single-tiered database model, a single application comprises the user interface. It uses a TClientDataSet component to create a database-engine independent dataset that reads data from and writes data to flat files on disk without requiring a separate database engine or database server. The following figure illustrates the architectural model for a single-tiered application that uses a client dataset:

Single-tiered database application architecture

Two methods belonging to TClientDataSet, LoadFromFile, and SaveToFile, enable a client dataset to read data directly from a flat file for display, and to write data from data-aware controls directly to a flat file. In a sense, a client dataset component fills in for a database engine without requiring your application to have one. The structure of the fields defined for a client dataset determines how data is written to and read from disk. For more information about creating a single-tiered database application using TClientDataSet, see "Creating and using a client dataset."

Note:	TClientDataSet is also used--in a somewhat different manner--in the client portion of a multi-tiered database application. For an overview of building multi-tiered database applications with Delphi, see "Creating a multi-tiered application,"


Understanding the architecture of a two-tiered application

Section topics 	See also

In the two-tiered database model, a client application provides a user interface to data, and interacts directly with a local or remote database server through the BDE.

The following two-tiered database application architecture illustrates this relationship:

In this model, all Delphi applications are database clients. A client requests information from and sends information to a database server. A server can process requests from many clients simultaneously, coordinating access to and updating of data.

A well-designed two-tiered application makes it easy for a user to view and edit data, even if the application works with a complex data model. As an applications developer, you must understand the complexities of your data models even as you work to mask them from the eventual end-users of your application.

The following figure illustrates how Delphi's tools work together in the Client/Server Suite to enable you to create robust two-tiered applications.

The Delphi Client/Server Suite two-tiered development environment:

Even if you have Delphi Professional or Delphi Standard, the investment you make in building database applications is protected when you upgrade to Delphi Client/Server Suite. Delphi simplifies database application development so that you can migrate to a larger, enterprise-wide, client application/database server implementation as the need arises.


Understanding the architecture of a multi-tiered application

Section topics 	See also

In the multi-tiered database model, an application is partitioned into pieces that reside on different machines. A client application provides a user interface to data. It passes all data requests and updates through an application server (also called a "remote data broker"). The application server, in turn, communicates directly with a remote database server. Usually in this model, the client application, the application server, and the remote database server are on separate machines. The following multi-tiered database architecture illustrates this relationship.

You can use Delphi to create both client applications and application servers. As illustrated, a client application uses standard data-aware controls connected through a data source to one or more client dataset components in order to display data for viewing and editing. Each client dataset communicates with an application server through an IProvider interface that is part of the application server's remote data module. The IProvider interface is an ActiveX dual interface that is initially established by the application server when a client first uses its TMIDASConnection component to establish an initial connection to the application server.

An application server creates IProvider interfaces in one of two ways. If the application server includes one or more TProvider objects, then these objects are used to create the IProvider interface. If not, the application server creates IProvider interfaces automatically. (In general, using a TProvider component instead of relying on Delphi to create the interface gives an application more control over the interface.) In either case, all data is passed between the client application and the application server through the interface. The interface receives data from and sends updates to conventional Delphi dataset components, and these components communicate with a database server through the BDE.

Generally, there are several client applications that communicate with a single application server in the multi-tiered model. The application server provides a gateway to your databases for all your client applications, and it lets you provide enterprise-wide database tasks in a central location, accessible to all your clients.


Extending Delphi's database architecture

Section topics 	See also

In addition to all the built-in ways of creating database applications, Delphi's new open dataset model enables you to extend the architecture to add capabilities specific to your needs. In the extended dataset model, you determine your needs, and derive a custom dataset component from TDataSet or one of its descendants. Deriving custom components from TDataSet lets you use Delphi's data-aware controls to create your UI so that you can focus on database engine tasks that will be invisible to your users.

For example, if you want to maintain a thin client application, such as those you can create with using the new TClientDataSet, but you want to connect to a DB2 database without using the BDE, then you need to create a custom dataset component. TDataSet defines functionality common to all dataset objects. You may discover that an existing dataset object, such as TClientDataSet, offers additional features that you want to incorporate. If so, then you might want to derive you custom dataset object from TClientDataSet instead of TDataSet. Start with an existing component that is as feature complete as possible to reduce the amount of coding you need to do. It's up to you. Delphi's open dataset architecture offers many extensible paths.


See also

Creating a multi-tiered application

Creating and using a client dataset

Working with field components

Using data controls

Working with grids


Managing database sessions

Section topics 	See also

Both standalone, full client database applications and database application servers communicate with databases through the Borland Database Engine (BDE). An application's database connections, drivers, cursors, queries, and so on are maintained within the context of one or more BDE sessions. Sessions isolate a set of database access operations, such as database connections, without the need to start another instance of the application.

In a Delphi application, you can manage BDE sessions using TSession and TSessionList components. Each TSession component in an application encapsulates a single BDE session. All sessions within an application are managed by a single TSessionList component.

All database applications are automatically provided with a session component, named Session, that encapsulates the default BDE session. Applications can declare, create, and manipulate additional session components as needed.

All database applications also are automatically provided with a session list component, named Sessions, that enables the application to manage all of its session components.

These topics describe the session and session list components and explains how to use them to control BDE sessions in your full client database applications and database application servers.

Note:	The default session and session list components provide a widely applicable set of defaults that can be used as is by most applications. Only applications that use multiple sessions (for example, because of the need to run concurrent queries against a single database) may need to manipulate its session and session list components.


Working with a session component

Section topics 	See also

A session component provides global management over a group of database connections within an application. When you create a full client database application or an application server, your application automatically contains a session component, named Session. As you add database and dataset components to the application, they are automatically associated with this default session. It also controls access to password protected Paradox files, and it specifies directory locations for sharing Paradox files over a network. Applications can control database connections and access to Paradox files by using the properties, events, and methods of the session.

You can use the default session to control all the database connections in an application, or you can either add or *create additional session components at design time or create them dynamically at run time to control a subset of database connections in an application.

Some applications require additional session components, such as applications that run concurrent queries against the same database. In this case, each concurrent query must run under its own session. Multi-threaded database applications also require multiple sessions. Applications that use multiple sessions must manage those sessions through the session list component, Sessions. For more information about managing multiple sessions see "Managing multiple sessions."


Using the default session

Section topics 	See also

All database applications are automatically provided with a default session. Delphi creates a default session component named Session for a database application each time it runs (note that its SessionName is "Default"). The default session provides global control over all database components not associated with another session, whether they are temporary (created by the session at run time when a dataset is opened that is not associated with a database component you create), or persistent (explicitly created by your application). The default session is not visible in your data module or form at design time, but you can access its properties and methods in your code at run time.

When you create a database component, it is automatically associated with the default session. You need only associate a database component that you create with an explicitly named session if the component performs a simultaneous query against a database already opened by the default session. In this case, you are creating a multi-threaded database application, and must create one additional session to handle each additional thread.

To use the default session, you need write no code unless your application must:

Modify the properties of the session, such as specifying when database connections for automatically generated database components should automatically be kept or dropped.

Respond to session events, such as when the application attempts to access a password-protected Paradox file.

Execute a session's methods, such as opening or closing a database in response to user-initiated actions.

Set the NetFileDir property to access Paradox tables on a network and set the PrivateDir property to a local hard drive to speed performance.

When you add database components to an application at design time, or create them dynamically at run time, they are automatically associated with the default session unless you specifically assign them to a different session. If your application attempts to open a dataset that is not associated with a database component, then Delphi automatically creates a database component for it at run time, associates the database component with the default session, and assigns initial values to some of the database component's key properties based on the default session's properties.

Among the most important of these properties is KeepConnections, which determines when database connections are maintained or dropped by an application. For more information about KeepConnections, see "Specifying default database connection behavior." Additional properties, events, and methods of the TSession component which apply to the default session and any additional sessions you create in your applications are described throughout the remainder of these topics.


Creating additional sessions

Section topics 	See also

You can create your own sessions to supplement the default session. At design time you can place additional sessions on a data module (or form), set their properties in the Object Inspector, write event handlers for them, and write code that calls their methods. You can also create sessions, set their properties, and call their methods at run time. This topic describes how to create and free sessions at run time.

Note:	Keep in mind that creating additional sessions is optional unless an application runs concurrent queries against a database or the application is multi-threaded.

To enable dynamic creation of a session component at run time:

1.	Declare a TSession variable.

2.	Call the Create constructor to instantiate a new session. Create sets up an empty list of database components for the session, sets up an empty list of BDE callbacks for the session, sets the KeepConnections property to True, and adds the session to the list of sessions maintained by the application's session list component.

3.	Set the SessionName property for the new session to a unique name. This property is used to associate database components with the session. For more information about the SessionName property, see "Naming a session."

4.	Activate the session and optionally adjust its properties.

The following code illustrates these steps:

var

 SecondSession: TSession;

begin

 SecondSession := TSession.Create;

 with SecondSession do

   try

     SessionName := 'SecondSession';

     KeepConnections := False;

     Open;

 end;

 .

 .

 .

   finally

     SecondSession.Free;

 end;

end;

Note:	Never call Free for the default session.

You can also manage creating and opening of sessions using the OpenSession method of TSessionList. Using OpenSession is safer than calling Create, because OpenSession creates a session only if it does not already exist. For more information about using OpenSession, see "Managing multiple sessions."


Naming a session

Section topics 	See also

A session's SessionName property is used to name the session so that you can associate databases and datasets with it. For the default session, SessionName is "Default". For each additional session component you create, you must set its SessionName property to a unique value.

Database and dataset components have SessionName properties that correspond to the SessionName property of a session component. If you leave the SessionName property blank for a database or dataset component, it is automatically associated with the default session. You can also set SessionName for a database or dataset component to a name that corresponds to the SessionName of a session component you create. For example, the following code uses the OpenSession method of the default TSessionList component, Sessions, to open a new session component, sets its SessionName to "InterBaseSession", activates the session, and associates an existing database component Database1 with that session:

var

 IBSession: TSession;

...

begin

 IBSession := Sessions.OpenSession('InterBaseSession');

 Database1.SessionName := 'InterBaseSession';

end;

For more information about using the TSessionList component--and Sessions in particular--to control multiple sessions, see "Managing multiple sessions."


Activating a session

Section topics 	See also

Active is a Boolean property that determines if database and dataset components associated with a session are open. You can use this property to read the current state of a session's database and dataset connections, or to change it.

To determine the current state of a session, check Active. If Active is False (the default), all databases and datasets associated with the session are closed. If True, databases and datasets are open.

Setting Active to True calls a session's OnStartup event, sets the NetFileDir and PrivateDir properties if they are assigned values, and sets the ConfigMode property. You can write an OnStartup event handler to perform other specific database start-up activities. For more information about OnStartup, see "Working with password-protected Paradox tables." The NetFileDir and PrivateDir properties are used only for connecting to Paradox tables. For more information about them, see "Specifying the control file location" and"Specifying a temporary files location." ConfigMode determines how the BDE handles BDE aliases created within the context of the session. For more information about ConfigMode, see "Specifying alias visibility." To open database components within a session, see "Creating, opening, and closing database connections."

After you activate a session you can open its database connections by calling the OpenDatabase method.

For session components you place in a data module or form, setting Active to False when there are open databases or datasets closes them. At run time, closing databases and datasets may invoke events associated with them.

Note:	You cannot set Active to False for the default session at design time. While you can close the default session at run time, it is not recommended.

For session components you create, use the Object Inspector to set Active to False at design time to disable all database access for a session with a single property change. You might want to do this if, during application design, you do not want to receive exceptions because a remote database is temporarily unavailable.

You can also use a session's Open and Close methods to activate or deactivate sessions other than the default session at run time. For example, the following code closes all open databases and datasets for a session with a single line of code:

Session1.Close;

This code sets the Session1's Active property to False. When a session's Active property is False, any subsequent attempt by the application to open a database or dataset resets Active to True and calls the session's OnStartup event if it exists. You can also explicitly code session reactivation at run time. The following code reactivates Session1:

Session1.Open;

Note:	If a session is active you can also open and close individual database connections. For more information, see "Closing a single database connection."


Customizing session start-up

Section topics 	See also

To customize a session's start-up behavior, write an OnStartup event handler for it. OnStartup is triggered when a session is activated. A session is activated when it is first created, and subsequently, whenever its Active property is changed to True from False (for example, when a database or dataset is associated with a session is opened and there are currently no other open databases or datasets).


Specifying default database connection behavior

Section topics 	See also

KeepConnections provides the default value for the KeepConnection property of temporary database components created at run time. KeepConnection specifies what happens to a database connection established for a database component when all its datasets are closed. If True (the default), a constant, or persistent, database connection is maintained even if no dataset is active. If False, a database connection is dropped as soon as all its datasets are closed.

Note:	Connection persistence for a database component you explicitly place in a data module or form is controlled by that database component's KeepConnection property. If they differ in setting, KeepConnection for a database component always overrides the KeepConnections property of the session. For more information about controlling individual database connections within a session, see "Creating, opening, and closing database connections."

KeepConnections should always be set to True for applications that frequently open and close all datasets associated with a database on a remote server. This setting reduces network traffic and speeds data access because it means that a connection need be opened and closed only once during the lifetime of the session. Otherwise, every time the application closes or reestablishes a connection, it incurs the overhead of attaching and detaching the database.

Note:	Even when KeepConnections is True for a session, you can close inactive database connections for all temporary database components, and then free the temporary database components by calling the DropConnections method. For more information about DropConnections, see "Dropping temporary database connections."

For example, the following code drops inactive connections and frees all temporary database components for the default session:

Session.DropConnections;


Creating, opening, and closing database connections

Section topics 	See also

To open a database connection within a session, call the OpenDatabase method. OpenDatabase takes one parameter, the name of the database to open. This name is a BDE alias or the name of a database component. For Paradox or dBASE, the name can also be a fully-qualified path name. For example, the following statement uses the default session and attempts to open a database connection for the database pointed to by the DBDEMOS alias:

var

 DBDemosDatabase: TDatabase;

begin

 Session.OpenDatabase('DBDEMOS');

 .

 .

 .

OpenDatabase makes its own session active if it is not already active, and then determines if the specified database name matches the DatabaseName property of any database components for the session. If the name does not match an existing database component, OpenDatabase creates a temporary database component using the specified name. Each call to OpenDatabase increments a reference count for the database by 1. As long as this reference count remains greater than 0, the database is open. Finally, OpenDatabase calls the Open method of the database component to connect to the server.

Closing a single database connection

You can close an individual database connection with the CloseDatabase method, or close all connections for a session at once with the Close method. When you call CloseDatabase, a reference count for the database is decremented by 1. When the reference count for the database is 0, the database is closed and freed. CloseDatabase takes one parameter, the name of the database to close. This name is a BDE alias, database component name (for Paradox and dBASE it can also be a fully-qualified path name). For example, the following statement closes the database connection specified by the DBDEMOS alias:

CloseDatabase('DBDEMOS');

If the specified database name is associated with a temporary database component, and the session's KeepConnections property is False, the temporary database component is freed, effectively closing the connection.

Note:	If KeepConnections is False temporary database components are closed and freed automatically when the last dataset associated with the database component is closed. An application can always call CloseDatabase prior to that time to force closure. To free temporary database components when KeepConnections is True, call the database component's Close method, and then call the session's DropConnections method.

If the database component is persistent (meaning that the application specifically declares the component and instantiates it), and the session's KeepConnections property is False, CloseDatabase calls the database component's Close method to close the connection.

Note:	Calling CloseDatabase for a persistent database component does not actually close the connection. To close the connection, call the database component's Close method directly.

Closing all database connections

There are two ways to close all database connections at once:

Set the Active property for the session to False.

Call the Close method for the session.

When you set Active to False, Delphi automatically calls the Close method. Close disconnects from all active databases by freeing temporary database components and calling each persistent database component's Close method. Finally, Close sets the session's BDE handle to nil.


Dropping temporary database connections

Section topics 	See also

If the KeepConnections property for a session is True (the default), then database connections for temporary database components are maintained even if all the datasets used by the component are closed. You can eliminate these connections and free all inactive temporary database components for a session by calling the DropConnections method. For example, the following code frees all inactive, temporary database components for the default session:

Session.DropConnections;

Temporary database components for which one or more datasets are active are not dropped or freed by this call. To free these components, call Close.


Searching for a database connection

Section topics 	See also

Use a session's FindDatabase method to determine whether or not a specified database component is already associated with a session. FindDatabase takes one parameter, the name of the database to search for. This name is a BDE alias or database component name. For Paradox or dBASE, the name can also be a fully-qualified path name. For example, the following statement searches the default session for a database component using the DBDEMOS alias, and if it is not found, creates and opens it:

var

 DB: TDatabase;

begin

 DB := Session.FindDatabase('DBDEMOS');

 if DB = nil then {database doesn't exist for session so,}

   DB := Session.OpenDatabase('DBDEMOS'); {create and open it}

 if Assigned(DB) and DB.Active then

 begin

   DB.StartTransaction;

   .

   .

   .

 end;

end;

FindDatabase returns the database component if it finds a match. Otherwise it returns nil.


Retrieving information about a session

Section topics 	See also

You can retrieve information about a session and its database components by using a session's informational methods. For example, one method retrieves the names of all aliases known to the session, and another method retrieves the names of tables associated with a specific database component used by the session. The following table summarizes the informational methods to a session component:

Method	Purpose
GetAliasDriverName	Retrieves the BDE driver for a specified alias of a database.
GetAliasNames	Retrieves the list of BDE aliases for a database.
GetAliasParams	Retrieves the list of parameters for a specified BDE alias of a database.
GetConfigParams	Retrieves specific configuration information from the BDE configuration file.
GetDatabaseNames	Retrieves the list of BDE aliases and the names of any TDatabase components currently in use.
GetDriverNames	Retrieves the names of all currently installed BDE drivers.
GetDriverParams	Retrieves the list of parameters for a specified BDE driver.
GetStoredProcNames	Retrieves the names of all stored procedures for a specified database.
GetTableNames	Retrieves the names of all tables matching a specified pattern for a specified database.

Except for GetAliasDriverName, these methods return a set of values into a string list declared and maintained by your application. (GetAliasDriverName returns a single string, the name of the current BDE driver for a particular database component used by the session.)

For example, the following code retrieves the names of all database components and aliases known to the default session:

var

 List: TStringList;

begin

 List := TStringList.Create;

 try

   Session.GetDatabaseNames(List);

   .

   .

   .

 finally

   List.Free;

 end;

end;

For a complete description of a session's informational methods, see the online reference for the object and component library.


Working with BDE aliases

Section topics 	See also

Because a session usually encapsulates a series of database connections, one property and many of a session component's methods work with BDE aliases. Each database component associated with a session has a BDE alias (although optionally a fully-qualified path name may be substituted for an alias when accessing Paradox and dBASE tables. The purpose of this property and these methods can be broken into three groups:

Determining visibility

Retrieving alias and driver information

Creating, modifying, and deleting aliases

The following sections describe these functional areas.

Specifying alias visibility

A session's ConfigMode property determines what BDE aliases are visible to the session. ConfigMode is a set that describes which types of sessions are visible. The default setting is cmAll, which translates into the set [cfmVirtual, cfmPersistent]. If ConfigMode is cmAll, a session can see all aliases created within the session, all aliases in the BDE configuration file on a user's system, and all aliases that the BDE maintains in memory.

The main purpose of ConfigMode is to enable an application to specify and restrict alias visibility at the session level. For example, setting ConfigMode to [cfmSession] restricts a session's view of aliases to those created within the session. All other aliases in the BDE configuration file and in memory are not available.

For a full description of ConfigMode and its settings, see the online reference for the object and component library.

Making session aliases visible to other sessions and applications

When an alias is created during a session, the BDE stores a copy of the alias in memory. By default this copy is local only to the session in which it is created. Another session in the same application can see the alias only if its ConfigMode property is cmAll or [cfmPersistent].

To make an alias available to all sessions and to other applications, use the session's SaveConfigFile method. SaveConfigFile writes aliases in memory to the BDE configuration file where they can be read and used by other BDE-enabled applications.

Determining known aliases, drivers, and parameters

Five methods for session components enable an application to retrieve information about BDE aliases, including parameter information and driver information. They are:

GetAliasNames, to list the aliases to which a session has access.

GetAliasParams, to list the parameters for a specified alias.

GetAliasDriverName, to return a string containing the name of the BDE driver used by the alias.

GetDriverNames, to return a list of all BDE drivers available to the session.

GetDriverParams, to return driver parameters for a specified driver.

For more information about using a session's informational methods, see "Retrieving information about a session."

Creating, modifying, and deleting aliases

A session can create, modify, and delete aliases during its lifetime. The AddAlias method creates a new BDE alias for an SQL database sever. AddStandardAlias creates a new BDE alias for Paradox, dBASE, or ASCII tables.

AddAlias takes three parameters: a string containing a name for the alias, a string that specifies the SQL Links driver to use, and a string list populated with parameters for the alias. For example, the following statements adds a new alias for accessing an InterBase server to the default session:

var

 AliasParams: TStringList;

begin

 AliasParams := TStringList.Create;

 try

   with AliasParams do

     Add('OPEN MODE=READ');

     Add('USER NAME=TOMSTOPPARD');

     Add('SERVER NAME=ANIMALS:/CATS/PEDIGREE.GDB');

   end;

   Session.AddAlias('CATS', 'INTRBASE', AliasParams);

   .

   .

   .

 finally

     AliasParams.Free;

 end;

end;

For more information about AddAlias, see TSession.AddAlias.

AddStandardAlias takes three string parameters: the name for the alias, the fully-qualified path to the Paradox or dBASE table to access, and the name of the default driver to use when attempting to open a table that does not have an extension. For example, the following statement creates a new alias for accessing a Paradox table:

AddStandardAlias('MYDBDEMOS', 'C:\TESTING\DEMOS\', 'Paradox');

For more information about AddStandardAlias, see TSession.AddStandardAlias.

Note:	When you add an alias to a session, it is available only to this session and any other sessions with [cfmPersistent] included in set of configuration modes for the ConfigMode property. To make a newly created alias available to all applications, call SaveConfigFile after creating the alias. For more information about ConfigMode, see "Working with BDE aliases."

After you create an alias, you can make changes to its parameters by calling ModifyAlias. ModifyAlias takes two parameters: the name of the alias to modify, and a string list containing the parameters to change and their values. For example, the following statements change the OPEN MODE parameter for the CATS alias to READ/WRITE in the default session:

var

 List: TStringList;

begin

 List := TStringList.Create;

 with List do

 begin

   Clear;

   Add('OPEN MODE=READ/WRITE');

 end;

 Session.ModifyAlias('CATS', List);

 List.Free;

 .

 .

 .

To delete an alias previously created in a session, call the DeleteAlias method. DeleteAlias takes one parameter, the name of the alias to delete. DeleteAlias makes an alias unavailable to the session.

Note:	DeleteAlias does not remove an alias from the BDE configuration file if the alias was written to the file by a previous call to SaveConfigFile. To remove the alias from the configuration file after calling DeleteAlias, call SaveConfigFile again.


Iterating through a session's database components

Section topics 	See also

Two session component properties, Databases and DatabaseCount, enable you to cycle through all the database components associated with a session.

Databases is an array of all currently active database components associated with a session. Used with the DatabaseCount property, Databases can be used to iterate through all active database components to perform a selective or universal action.

DatabaseCount is an integer property that indicates the number of currently active databases associated with a session. As connections are opened or closed during a session's life-span, this number can change. For example, if a session's KeepConnections property is False and all database components are created by Delphi as needed at run time, then each time a unique database is opened, DatabaseCount increases by one. Each time a unique database is closed, DatabaseCount decreases by one. If DatabaseCount is zero, there are no currently active database components for the session.

DatabaseCount is typically used with the Databases property to perform actions common to active database components. For example, the following code sets the KeepConnection property of each active database in the default session to True:

var

 MaxDbCount: Integer;

begin

 with Session do

   if DatabaseCount > 0 then

     for MaxDbCount := 1 to DatabaseCount do

       Databases{MaxDbCount].KeepConnection := True;

 end;

end;


Specifying Paradox directory locations

Section topics 	See also

Two session component properties, NetFileDir, and PrivateDir are specific to applications that work with Paradox tables. NetFileDir specifies the directory that contains the Paradox network control file, PDOXUSRS.NET. This file governs sharing of Paradox tables on network drives. All applications that need to share Paradox tables must specify the same directory for the network control file (typically a directory on a network file server).

PrivateDir specifies the directory for storing temporary table processing files, such as those generated by the BDE to handle local SQL statements.

Specifying the control file location

Delphi derives a value for NetFileDir from the Borland Database Engine (BDE) Configuration file for a given database alias. If you set NetFileDir yourself, the value you supply overrides the BDE Configuration setting, so be sure to validate the new value.

At design time you can specify a value for NetFileDir in the Object Inspector. You can also set or change NetFileDir in code at run time. The following code sets NetFileDir for the default session to the location of the directory from which your application runs:

Session.NetFileDir := ExtractFilePath(ParamStr(0));

Note:	NetFileDir can be changed only when an application does not have any open Paradox files. If you change NetFileDir at run time, verify that it points to a valid network directory that is shared by your network users.

Specifying a temporary files location

If no value is specified for the PrivateDir property, the BDE automatically uses the current directory at the time it is initialized. If your application runs directly from a network file server, you can improve application performance at run time by setting PrivateDir to a user's local hard drive before opening the database.

Note:	Do not set PrivateDir at design time if you intend to open the database. Doing so generates a "Directory busy" error.

The following code changes the setting of the default session's PrivateDir property to a user's C:\TEMP directory:

Session.PrivateDir := 'C:\TEMP';

Important:	Do not set PrivateDir to a root directory on a drive. Alway specify a subdirectory.


Working with password-protected Paradox tables

Section topics 	See also

A session component provides four methods and one event that are exclusively used to manage access to password-protected Paradox files. The methods are AddPassword, GetPassword, RemoveAllPasswords, and RemovePassword. The event is OnPassword.

The AddPassword procedure provides an optional way for an application to provide a password for a session prior to opening an encrypted Paradox table that requires a password for access. AddPassword takes one parameter, a string containing the password to use. You can call AddPassword as many times as necessary to add passwords to access Paradox files protected with different passwords.

Note:	If you do not call AddPassword to specify one or more passwords to use when accessing password-protected Paradox files, then when your application attempts to open such a table and your application does not have an OnPassword event handler, Delphi displays a dialog box that prompts the user for a password.

RemovePassword deletes a previously added password from memory. RemovePassword takes one parameter, a string containing the password to delete. RemoveAllPasswords deletes all previously added passwords from memory.

GetPassword triggers the OnPassword event for a session. The OnPassword event is called only when an application attempts to open a Paradox table for the first time and the BDE reports insufficient access rights. You can code an OnPassword event to handle and provide a password to the BDE, or you can choose to use Delphi's default password handling, which displays a dialog box prompting the user for a password.


Managing multiple sessions

Section topics 	See also

If you create a single application that uses multiple threads that perform database operations, then you must create one additional session for each thread. The Data Access page on the Component palette contains a session component that you can place in a data module or on a form at design time.

Important:	When you place a session component, you must also set its SessionName property to a unique value so that it does not conflict with the default session's SessionName property.

Placing a session component at design time presupposes that the number of threads (and therefore sessions) required by the application at run time is static. More likely, however, is that an application needs to create sessions dynamically. To create sessions dynamically, call the global function Sessions.OpenSession at run time.

Sessions.OpenSession requires a single parameter, a name for the session that is unique across all session names for the application. The following code dynamically creates and activates a new session with a uniquely generated name:

Sessions.OpenSession('RunTimeSession' + IntToStr(Sessions.Count + 1));

This statement generates a unique name for a new session by retrieving the current number of sessions, and adding one to that value. Note that if you dynamically create and destroy sessions at run time, this example code will not work as expected. Nevertheless, this example illustrates how to use the properties and methods of Sessions to manage multiple sessions.

Sessions is a component of type TSessionList that is automatically instantiated for database applications. You use the properties and methods of Sessions to keep track of multiple sessions in a multi-threaded database application. The following table summarizes the properties and methods of the TSessionList component:

Property or  Method	Purpose
Count	Returns the number of sessions, both active and inactive, in the sessions list.
FindSession	Searches the session list for a session with a specified name, and returns a pointer to the session component, or nil if there is no session with the specified name. If passed a blank session name, FindSession returns a pointer to the default session, Session.
GetSessionNames	Returns a string list containing the names of all currently instantiated session components. This procedure always returns at least one string, 'Default' for the default session (note that the default session's name is actually a blank string).
List	Returns the session component for a specified session name. If there is no session with the specified name, an exception is raised.
OpenSession	Creates and activates a new session or reactivates an existing session for a specified session name.
Sessions	Accesses the session list by ordinal value. Sessions is the default property, so you can call it like so Sessions[0], rather than Sessions.Sessions[0].

As an example of using Sessions properties and methods in a multi-threaded application, consider what happens when you want to open a database connection. To determine if a connection already exists, use the Sessions property to walk through each session in the sessions list, starting with the default session. For each session component, examine its Databases property to see if the database in question is open. If you discover that another thread is already using the desired database, examine the next session in the list.

If an existing thread is not using the database, then you can open the connection within that session.

If, on the other hand, all existing threads are using the database, you must open a new session in which to open another database connection.


Using a session component in data modules

Section topics 	See also

You can safely place session components in data modules. If you put a data module that contains one or more session components into the Object Repository, however, other users can copy your data module, but they cannot inherit from it. These components share global name space, and inheriting from them results in global name space conflicts.


See also

TSession

TSessionList

Data Access components

TDataModule

User’s Guide, Chapter 7: Using data modules


Connecting to databases

Section topics 	See also

When a Delphi application connects to a database, that connection is encapsulated by a TDatabase component. A database component encapsulates the connection to a single database in the context of a Borland Database Engine (BDE) session in an application. These topics describe database components and how to use them to manipulate database connections and control transactions.


Understanding persistent and temporary database components

Section topics 	See also

Each database connection in an application is encapsulated by a database component whether or not you explicitly provide a database component at design time or create it dynamically at run time. When an application attempts to connect to a database, Delphi either uses an explicitly instantiated, or persistent, database component, or it generates a temporary database component that generally exists only for the lifetime of the connection.

Delphi creates temporary database components as necessary for any datasets in a data module or form for which you do not create them yourself. Temporary database components provide broad support for many typical desktop database applications without requiring you to handle the details of the database connection. For most client/server applications, however, you should create your own database components instead of relying on temporary ones. You gain greater control over your databases, including the ability to:

Create persistent database connections.

Customize database server logins.

Control transactions and specify transaction isolation levels.

Create BDE aliases local to your application.


Using temporary database components

Section topics 	See also

Temporary database components are automatically generated by Delphi as needed. For example, if you place a TTable component on a form, set its properties, and open the table without first placing and setting up a TDatabase component and associating the table component with it, Delphi creates a temporary database component for you behind the scenes.

Some key properties of temporary database components are determined by the session to which they belong. For example, the controlling session's KeepConnections property determines whether a database connection is maintained even if its associated datasets are closed (the default), or if the connections are dropped when all its datasets are closed. Similarly, the default OnPassword event for a session guarantees that when an application attempts to attach to a database on a server that requires a password, it displays a standard password prompt dialog box. Other properties of temporary database components provide standard login and transaction handling. For more information about sessions and session control over temporary database connections, see "Working with a session component."

The default properties created for temporary database components provide reasonable, general behaviors meant to cover a wide variety of situations. For complex, mission-critical client/server applications with many users and different requirements for database connections, however, you should create your own database components to tune each database connection to your application's needs.


Creating database components at design time

Section topics

The Data Access page of the Component palette contains a database component you can place in a data module or form. The main advantages to creating a database component at design time are that you can set its initial properties and write OnLogin events for it. OnLogin offers you a chance to customize the handling of security on a database server when a database component first connects to the server. For more information about managing connection properties, see "Connecting to a database server." For more information about server security, see "Controlling server login."


Creating database components at run time

Section topics 	See also

You can create database components dynamically at run time. An application might do this when the number of database components needed at run time is unknown, and your application needs explicit control over the database connection. In fact, this is how Delphi itself creates temporary database components as needed at run time. When you create a database component at run time, you need to give it a unique name and to associate it with a session.

You create the component by calling the TDatabase.Create constructor. Given both a database name and a session name, the following function creates a database component at run time, associates it with a specified session (creating a new session if necessary), and sets a few key database component properties:

function RunTimeDbCreate(const DatabaseName, SessionName: string): TDatabase;

var

 TempDatabase: TDatabase;

begin

 TempDatabase := nil;

 try

   { If the session exists, make it active; if not, create a new session }

   Sessions.OpenSession(SessionName);

   with Sessions do

     with FindSession(SessionName) do

       Result := FindDatabase(DatabaseName);

     if Result = nil then

       begin

         { Create a new database component }

         TempDatabase := TDatabase.Create(Self);

         TempDatabase.DatabaseName := DatabaseName;

         TempDatabase.SessionName := SessionName;

         TempDatabase.KeepConnection := True;

       end;

       Result := OpenDatabase(DatabaseName);

     end;

   end;

 except

   TempDatabase.Free;

   raise;

 end;

end;

The following code fragment illustrates how you might call this function to create a database component for the default session at run time:

var

 MyDatabase: array [1..10] of  TDatabase;

 MyDbCount: Integer;

begin

 { Initialize MyDbCount early on }

 MyDbCount := 1;

 ...

 { Later, create a database component at run time }

 begin

   MyDatabase[MyDbCount] := RunTimeDbCreate('MyDb' + IntToStr(MyDbCount), '');

   Inc(MyDbCount);

 end;

 ...

end;


Controlling connections

Section topics 	See also

Whether you create a database component at design time or run time, you can use the properties, events, and methods of TDatabase to control and change its behavior in your applications. The following sections describe how to manipulate database components. For details about all TDatabase properties, events, and methods, see the online reference for the object and component libraries.


Associating a database component with a session

Section topics 	See also

All database components must be associated with a BDE session. Two database component properties, Session and SessionName, establish this association.

SessionName identifies the session alias with which to associate a database component. When you first create a database component at design time, SessionName is set to "Default". Multi-threaded or reentrant BDE applications may have more than one session. At design time, you can pick a valid SessionName from the drop-down list in the Object Inspector. Session names in the list come from the SessionName properties of each session component in the application.

The Session property is a run-time, read-only property that points to the session component specified by the SessionName property. For example, if SessionName is blank or "Default", then the Session property references the same TSession instance referenced by the global Session variable. Session enables applications to access the properties, methods, and events of a database component's parent session component without knowing the session's actual name. This is useful when a database component is assigned to a different session at run time.

For more information about BDE sessions, see "Managing database sessions."


Specifying a BDE alias

Section topics 	See also

AliasName and DriverName are mutually-exclusive BDE-specific properties. AliasName specifies the name of an existing BDE alias to use for the database component. The alias appears in subsequent drop-down lists for dataset components so that you can link them to a particular database component. If you specify AliasName for a database component, any value already assigned to DriverName is cleared because a driver name is always part of a BDE alias.

Note:	You create and edit BDE aliases using the Database Explorer or the BDE Administration Utility. For more information about creating and maintaining BDE aliases, "Specifying a BDE alias."

DatabaseName enables you to provide an application-specific name for a database component. The name you supply is in addition to AliasName or DriverName, and is local to your application. DatabaseName can be a BDE alias, or, for Paradox and dBASE files, a fully-qualified path name. Like AliasName, DatabaseName appears in subsequent drop-down lists for dataset components to enable you to link them to a database component.

DriverName is the name of a BDE driver. A driver name is one parameter in a BDE alias, but you may specify a driver name instead of an alias when you create a local BDE alias for a database component using the DatabaseName property. If you specify DriverName, any value already assigned to AliasName is cleared to avoid potential conflicts between the driver name you specify and the driver name that is part of the BDE alias identified in AliasName.

At design time, to specify a BDE alias, assign a BDE driver, or create a local BDE alias, double-click a database component to invoke the Database Properties editor.

You can enter a DatabaseName in the Name edit box in the properties editor. You can enter an existing BDE alias name in the Alias name combo box for the Alias property, or you can choose from existing aliases in the drop-down list. The Driver name combo box enables you to enter the name of an existing BDE driver for the DriverName property, or you can choose from existing driver names in the drop-down list.

Note:	The Database Properties editor also enables you to view and set BDE connection parameters, and set the states of the LoginPrompt and KeepConnection properties. To work with connection parameters, see "Setting BDE alias parameters." To set the state of LoginPrompt, see "Controlling server login," and to set KeepConnection see "Connecting to a database server."

To set DatabaseName, AliasName, or DriverName at run time, include the appropriate assignment statement in your code. For example, the following code uses the text from an edit box to create a local alias for the database component Database1:

Database1.DatabaseName := Edit1.Text;


Setting BDE alias parameters

Section topics 	See also

At design time you can create or edit connection parameters in three ways:

Use the Database Explorer or BDE Admininstration utility to create or modify BDE aliases, including parameters.

Double-click the Params property in the Object Inspector to invoke the String List editor. To learn more about the String List editor, see the User's Guide.

Double-click a database component in a data module or form to invoke the Database Properties editor.

All of these methods edit the Params property for the database component. Params is a string list containing the database connection parameters for the BDE alias associated with a database component. Some typical connection parameters include path statement, server name, schema caching size, language driver, and SQL query mode.

When you first invoke the Database Properties editor, the parameters for the BDE alias are not visible. To see the current settings, click Defaults. The current parameters are displayed in the Parameter overrides memo box. You can edit existing entries or add new ones. To clear existing parameters, click Clear. Changes you make take effect only when you click OK.

At run time, an application can set alias parameters only by editing the Params property directly.


Controlling server login

Section topics 	See also

Most remote database servers include security features to prohibit unauthorized access. Generally, the server requires a user name and password login before permitting database access.

At design time, if a server requires a login, Delphi displays a standard Login dialog box that prompts you for a user name and password when you first attempt to connect to the database.

At run time, there are three ways you can handle a server's request for a login:

Set the LoginPrompt property of a database component to True (the default). Your application displays the standard Login dialog box when the server requests a user name and password.

Set the LoginPrompt to False, and include hard-coded USER NAME and PASSWORD parameters in the Params property for the database component. For example:

 USER NAME=SYSDBA

 PASSWORD=masterkey

Important:	Note that because the Params property is easy to view, this method compromises server security, so it is not recommended.

Write an OnLogin event for the database component, and use it to set login parameters at run time. OnLogin gets a copy of the database component's Params property, which you can modify. The name of the copy in OnLogin is LoginParams. Use the Values property to set or change login parameters as follows:

 LoginParams.Values['USER NAME'] := UserName;

 LoginParams.Values['PASSWORD'] := PasswordSearch(UserName);

On exit, OnLogin passes its LoginParams values back to Params, which is used to establish a connection.


Connecting to a database server

Section topics 	See also

There are two ways to connect to a database server using a database component:

Call the Open method.

Set the Connected property to True.

Setting Connected to True executes the Open method. Open verifies that the database specified by the DatabaseName or Directory properties exists, and if an OnLogin event exists for the database component, it is executed. Otherwise, the default Login dialog box appears.

Note:	When a database component is not connected to a server and an application attempts to open a dataset associated with the database component, the database component's Open method is first called to establish the connection. If the dataset is not associated with an existing database component, a temporary database component is created and used to establish the connection.

Once a database connection is established the connection is maintained as long as there is at least one active dataset. When there are no more active datasets, whether or not the connection is dropped depends on the setting of the database component's KeepConnection property.

KeepConnection determines if your application maintains a connection to a database even when all datasets associated with that database are closed. If True, a connection is maintained. For connections to remote database servers, or for applications that frequently open and close datasets, make sure KeepConnection is True to reduce network traffic and speed up your application. If False a connection is dropped when there are no active datasets using the database. If a dataset is later opened which uses the database, the connection must be reestablished and initialized.


Special considerations when connecting to a remote server

Section topics 	See also

When you connect to a remote database server in a Delphi application, the application uses the BDE and the Borland SQL Links driver to establish the connection. (The BDE can communicate with an ODBC driver that you supply.) You need to configure the SQL Links or ODBC driver for your application prior to making the connection. SQL Links and ODBC parameters are stored in the Params property of a database component. For information about SQL Links parameters, see the online SQL Links User's Guide. To edit the Params property, see "Setting BDE alias parameters."

Working with network protocols

As part of configuring the appropriate SQL Links or ODBC driver, you may need to specify the network protocol used by the server, such as SPX/IPX or TCP/IP, depending on the driver's configuration options. In most cases, network protocol configuration is handled using a server's client setup software. For ODBC it may also be necessary to check the driver setup using the ODBC driver manager.

Establishing an initial connection between client and server can be problematic. The following troubleshooting checklist should be helpful if you encounter difficulties:

Is your server's client-side connection properly configured?

If you are using TCP/IP:

Is your TCP/IP communications software installed? Is the proper WINSOCK.DLL installed?

Is the server's IP address registered in the client's HOSTS file? or

Is the Domain Name Services (DNS) properly configured?

Can you ping the server?

Are the DLLs for your connection and database drivers in the search path?

For more troubleshooting information, see the online SQL Links User's Guide and your server documentation.

Using ODBC

A Delphi application can use ODBC data sources (for example, Btrieve). An ODBC driver connection requires:

A vendor-supplied ODBC driver.

The Microsoft ODBC Driver Manager.

The BDE configuration utility.

To set up a BDE alias for an ODBC driver connection, use the BDE Administration utility.


Disconnecting from a database server

Section topics 	See also

There are two ways to disconnect from a server from a database component:

Set the Connected property to False.

Call the Close method.

Setting Connected to False calls Close. Close closes all open datasets and disconnects from the server. For example, the following code closes all active datasets for a database component and drops its connections:

Database1.Connected := False;

Note:	Close disconnects from a database server even if KeepConnection is True.


Closing datasets without disconnecting from a server

Section topics 	See also

There may be times when you want to close all datasets without disconnecting from the database server.

To close all open datasets without disconnecting from a server:

1.	Set the database component's KeepConnections property to True.

2.	Call the database component's CloseDataSets method.


Iterating through a database component's datasets

Section topics 	See also

A database component provides two properties that enable an application to iterate through all the datasets associated with the component: DataSets and DataSetCount.

DataSets is an indexed array of all active datasets (TTable, TQuery, and TStoredProc) for a database component. An active dataset is one that is currently open. DataSetCount is a read-only integer value specifying the number of currently active datasets.

You can use DataSets with DataSetCount to cycle through all currently active datasets in code. For example, the following code cycles through all active datasets to set the CachedUpdates property for each dataset of type TTable to True:

var

 I: Integer;

begin

 for I := 0 to DataSetCount - 1 do

   if DataSets[I] is TTable then

     DataSets[I].CachedUpdates := True;

end;


Handling transactions

Section topics 	See also

When you create a database application using Delphi, Delphi provides transaction control for all database access, even against local Paradox and dBASE tables. A transaction is a group of actions that must all be carried out successfully on one or more tables in a database before they are committed (made permanent). If one of the actions in the group fails, then all actions are rolled back (undone).

Transactions ensure database consistency even if there are hardware failures. It also maintains the integrity of data while permitting concurrent multiuser access.

For example, an application might update the ORDERS table to indicate that an order for a certain item was taken, and then update the INVENTORY table to reflect the reduction in inventory available. If there were a hardware failure after the first update but before the second, the database would be in an inconsistent state because the inventory would not reflect the order entered. Under transaction control, both statements would be committed at the same time. If one statement failed, then both would be undone (rolled back).


Using implicit transactions

Section topics 	See also

By default, Delphi provides implicit transaction control for your applications through the Borland Database Engine (BDE). When an application is under implicit transaction control, Delphi uses a separate transaction for each record in a dataset that is written to the underlying database. It commits each individual write operation, such as Post and AppendRecord.

Using implicit transaction control is easy. It guarantees both a minimum of record update conflicts and a consistent view of the database. On the other hand, because each row of data written to a database takes place in its own transaction, implicit transaction control can lead to excessive network traffic and slower application performance.

If you explicitly control transactions, you can choose the most effective times to start, commit, and roll back your transactions. When you develop client applications in a multi-user environment, particularly when your applications run against a remote SQL server, such as Sybase, Oracle, Microsoft SQL, or InterBase,® or remote ODBC-compliant databases, you should control transactions explicitly.

Note:	If you use cached updating, you may be able to minimize the number of transactions you need to use in your applications. For more information about cached updates, see "Working with cached updates."


Working with explicit transaction control

Section topics 	See also

There are two mutually exclusive ways to control transactions explicitly in a Delphi database application:

Use the methods and properties of the database component, such as StartTransaction, Commit, Rollback, InTransaction, and TransIsolation.

Use passthrough SQL in a TQuery component. Passthrough SQL is meaningful only in the Delphi Client/Server Suite, where you use SQL Links to pass SQL statements directly to remote SQL or ODBC servers. For more information about TQuery, see "Working with queries."

The main advantage to using the methods and properties of a database component to control transactions is that it provides a clean, portable application that is not dependent on a particular database or server.

The main advantage to passthrough SQL is that you can use the advanced transaction management capabilities of a particular database server, such as schema caching. To understand the advantages of your server's transaction management model, see your database server documentation.

Starting a transaction

When you start a transaction, all subsequent statements that read from and write to the database occur in the context of that transaction. Each statement is considered part of a group. Changes must be successfully committed to the database, or every change made in the group must be undone.

Grouping statements is useful when statements are dependent upon one another. Consider a bank transaction at an Automated Teller Machine (ATM). When a customer decides to transfer money from a savings account to a checking account, two changes must take place in the bank's database records:

1.	The savings account must be debited.

2.	The checking account must be credited.

If, for any reason, one of these actions cannot be completed, then neither action should take place. Because these actions are so closely related, they should take place within a single transaction.

To start a transaction in a Delphi application, call a database component's StartTransaction method:

DatabaseInterBase.StartTransaction;

All subsequent database actions take place in the context of the newly started transaction until the transaction is explicitly terminated by a subsequent call to Commit or Rollback.

How long should you keep a transaction going? Ideally, only as long as necessary. The longer a transaction is active, the more simultaneous users that access the database, and the more concurrent, simultaneous transactions that start and end during the lifetime of your transaction, the greater the likelihood that your transaction will conflict with another when you attempt to commit your changes.

Committing a transaction

To make database changes permanent, a transaction must be committed using a database component's Commit method. Executing a commit statement saves database changes and ends the transaction. For example, the following statement ends the transaction started in the previous code example:

DatabaseInterBase.Commit;

Note:	Commit should always be attempted in a try...except statement. If a transaction cannot commit successfully, you can attempt to handle the error, and perhaps retry the operation.

Rolling back a transaction

To discard database changes, a transaction must roll back its changes using Rollback. Rollback undoes a transaction's changes and ends the transaction. For example, the following statement rolls back a transaction:

DatabaseInterBase.Rollback;

Rollback usually occurs in:

Exception handling code when you cannot recover from a database error.

Button or menu event code, such as when a user clicks a Cancel button.

Using the TransIsolation property

TransIsolation specifies the transaction isolation level for a database component's transactions. Transaction isolation level determines how a transaction interacts with other simultaneous transactions when they work with the same tables. In particular, it affects how much a transaction "sees" of other transaction's changes to a table. Before changing or setting TransIsolation, you should be familiar with transactions and transactions management in Delphi.

The default setting for TransIsolation is tiReadCommitted. The following table summarizes possible values for TransIsolation and describes what they mean:

Isolation level	Meaning
tiDirtyRead	Permit reading of uncommitted changes made to the database by other simultaneous transactions. Uncommitted changes are not permanent, and might be rolled back (undone) at any time. At this level your transaction is least isolated from the changes made by other transactions.
tiReadCommitted	Permit reading only of committed (permanent) changes made to the database by other simultaneous transactions. This is the default isolation level.
tiRepeatableRead	Permit a single, one time reading of the database. Your transaction cannot see any subsequent changes to data by other simultaneous transactions. This isolation level guarantees that once your transaction reads a record, its view of that record will not change. At this level your transaction is most isolated from changes made by other transactions.

Database servers may support these isolation levels differently or not at all. If the requested isolation level is not supported by the server, then Delphi will use the next highest isolation level. The actual isolation level used by each type of server is shown in the folowing table. For a detailed description of how each isolation level is implemented, see your server documentation

Server	Specified Level	Actual Level
Oracle	tiDirtyRead tiReadCommitted tiRepeatableRead	tiReadCommitted tiReadCommitted tiRepeatableRead (READONLY)
Sybase, MS-SQL	tiDirtyRead tiReadCommitted tiRepeatableRead	tiReadCommitted tiReadCommitted Not supported
DB2	tiDirtyRead tiReadCommitted tiRepeatableRead	tiDirtyRead tiReadCommitted tiRepeatableRead
Informix	tiDirtyRead tiReadCommitted tiRepeatableRead	tiDirtyRead tiReadCommitted tiRepeatableRead
InterBase	tiDirtyRead tiReadCommitted tiRepeatableRead	tiReadCommitted tiReadCommitted tiRepeatableRead
Paradox, dBASE, Access, FoxPro	tiDirtyRead tiReadCommitted tiRepeatableRead	tiDirtyRead Not supported Not supported
Note:	When using transactions with local Paradox, dBASE, Access, and FoxPro tables, set TransIsolation to tiDirtyRead instead of using the default value of tiReadCommitted. A BDE error is returned if TransIsolation is set to anything but tiDirtyRead for local tables.

If an application is using ODBC to interface with a server, the ODBC driver must also support the isolation level. For more information, see your ODBC driver documentation.


Using passthrough SQL

Section topics 	See also

To use passthrough SQL to control a transaction you must:

Use the Delphi Client/Server Suite.

Install the proper SQL Links drivers. If you chose the Standard installation when installing Delphi, all SQL Links drivers are already properly installed.

Configure your network protocol correctly. See your network administrator for more information.

Have access to a database on a remote server.

Use the SQL Explorer to set SQLPASSTHRU MODE to NOT SHARED.

With passthrough SQL, you use a TQuery, TStoredProc, or TUpdateSQL component to send an SQL transaction control statement directly to a remote database server. The BDE does not process the SQL statement. Using passthrough SQL enables you to take direct advantage of the transaction controls offered by your server, especially when those controls are non-standard.

Setting SQLPASSTHRU MODE

SQLPASSTHRU MODE specifies whether the BDE and passthrough SQL statements can share the same database connections. In most cases, SQLPASSTHRU MODE is set to SHARED AUTOCOMMIT. If however, you want to pass SQL transaction control statements to your server, you must use the SQL Explorer to set the BDE SQLPASSTHRU MODE to NOT SHARED.

Note:	When SQLPASSTHRU MODE is NOT SHARED, you must use separate database components for TQuery components that pass SQL transaction statements to the server and those other dataset components that do not.


Using local transactions

Section topics 	See also

The BDE supports local transactions against Paradox and dBASE tables. From a coding perspective, there is no difference to you between a local transaction and a transaction against a remote database server.

When a transaction is started against a local table, updates performed against the table are logged. Each log record contains the old record buffer for a record. When a transaction is active, records that are updated are locked until the transaction is committed or rolled back. On rollback, old record buffers are applied against updated records to restore them to their pre-update states.

Understanding the limitations of local transactions

The following limitations apply to local transactions

Automatic crash recovery is not provided.

Data definition statements are not supported.

For Paradox, local transactions can be performed only on tables with valid indexes. Data cannot be rolled back on Paradox tables that do not have indexes.

Transactions cannot be run against temporary tables.

Transactions cannot be run against the BDE ASCII driver.

TransIsolation level must be set only to tiDirtyRead.

Closing a cursor on a table during a transaction rolls back the transaction unless:

Several tables are open.

The cursor is closed on a table to which no changes were made.


Applying cached updates for multiple datasets

Section topics 	See also

If your application uses cached updates, then you know that you ordinarily work with cached updates at the dataset level. A database component, however, has an ApplyUpdates method that enables you to apply cached updates for a specified set of dataset components instead of calling each dataset's own ApplyUpdates method individually.

ApplyUpdates takes one parameter, an array listing the datasets to update. For example, the following statement applies cached updates for two datasets associated with a database component:

Database1.ApplyUpdates([DataSet1, DataSet2];

This method starts a database transaction, and write the cached updates to the database server. Applying updates is a two-phase process:

1.	A database transaction starts.

2.	Cached updates are written to the database (Phase 1).

If the database write is successful:

1.	Database changes are committed, ending the transaction.

2.	Cached updates are committed, clearing the internal cache buffer (Phase 2).

If the database write fails, changes are rolled back, ending the transaction, and the cache is not cleared.

The two-phased approach allows for effective error recovery, especially when updating multiple and interrelated datasets (for example, the datasets associated with a master/detail form).

For more information about using cached updates, see "Working with cached updates."


Understanding database and session component interactions

Section topics 	See also

In general, session component properties, such as KeepConnections, provide global, default behaviors that apply to all temporary database components created by Delphi as needed at run time.

Session methods apply somewhat differently. TSession methods affect all database components, regardless of database component status. For example, the session method DropConnections closes all datasets belonging to a session's database components, and then drops all database connections, even if the KeepConnection property for individual database components is True.

Database component methods apply only to the datasets associated with a given database component. For example, suppose the database component Database1 is associated with the default session. Database1.CloseDataSets closes only those datasets associated with Database1. Open datasets belonging to other database components within the default session remain open.


Using database components in data modules

Section topics 	See also

You can safely place database components in data modules. If you put a data module that contains a database component into the Object Repository, however, other users can copy your data module, but they cannot inherit from it. These components share global name space, and inheriting from them results in global name space conflicts.


See also

TDatabase

Data Access components

User’s Guide, Chapter 7: Using data modules


Understanding datasets

Section topics 	See also

In Delphi the fundamental unit for accessing data is the dataset family of objects. Your application uses datasets for all database access. Generally, a dataset object represents a specific table belonging to a database, or it represents a query or stored procedure that accesses a database.

All dataset objects that you will use in your database applications descend from the virtualized dataset object, TDataSet, and they inherit data fields, properties, events, and methods from TDataSet. These topics describe the functionality of TDataSet that is inherited by the dataset objects you will use in your database applications. You need to understand this shared functionality in order to use any dataset object.

The following figure illustrates the hierarchical relationship of all Delphi dataset components:

Delphi dataset hierarchy

To understand the concepts common to all dataset objects, and to prepare for developing your own custom dataset objects that bypass the Borland Database Engine (BDE), read these topics.

To develop traditional, two-tier client/server database applications using the Borland Database Engine (BDE), see "Understanding BDE-enabled datasets." It introduces TBDEDataSet and TDBDataSet, and focuses on the shared features of TQuery, TStoredProc, and TTable, the dataset components used most commonly in all database applications.

If you have Delphi Client/Server or Delphi Enterprise, then you can develop multi-tier database applications using distributed datasets. To learn about working with multi-tier client datasets, see "Creating a multi-tiered application." It discusses how to use TClientDataSet and how to connect the client to an application server.


What is TDataSet?

Section topics 	See also

TDataSet is the ancestor for all dataset objects you will use in your applications. It defines a set of data fields, properties, events, and methods shared by all dataset objects. TDataSet is a virtualized dataset, meaning that many of its properties and methods are declared as virtual or abstract. An abstract method is a function or procedure declaration without an actual implementation. The declaration is a prototype that describes a method (and its parameters and return type, if any) that must be implemented in all descendant dataset objects, but that might be implemented differently by each of them. A virtual method that is not also abstract is a function or procedure declaration and an implementation of that method that can be (and usually is) overridden in a descendant object.

Because TDataSet contains abstract methods, you cannot use it directly in an application without generating a run-time error. Instead, you either create instances of TDataSet's descendants, such as TTable, TQuery, or TStoredProc, and use them in your application, or you derive your own dataset object from TDataSet or its descendants and write implementations for all its abstract methods.

Nevertheless, TDataSet defines much that is common to all dataset objects. For example, TDataSet defines the basic structure of all datasets: an array of TField components that correspond to actual columns in one or more database tables, lookup fields provided by your application, or calculated fields provided by your application. For more information about TField components, see "Working with field components."

These topics describe the following features of TDataSet common to all dataset objects:

Opening and closing datasets

Determining and setting dataset states

Navigating among records

Searching for records

Filtering datasets

Editing and modifying records


Opening and closing datasets

Section topics 	See also

To read or write data in a table or through a query, an application must first open a dataset. There are two ways to open a dataset:

Set the Active property of the dataset to True, either at design time in the Object Inspector, or in code at run time:

CustTable.Active := True;

Call the Open method for the dataset at run time:

CustQuery.Open;

There are also two ways to close a dataset:

Set the Active property of the dataset to False, either at design time in the Object Inspector, or in code at run time:

CustQuery.Active := False;

Call the Close method for the dataset at run time:

CustTable.Close;

You may need to close a dataset when you want to change certain of its properties, such as TableName on a TTable component. At run time, you may also want to close a dataset for other reasons specific to your application.


Determining and setting dataset states

Section topics 	See also

The state--or mode--of a dataset determines what can be done to its data. For example, when a dataset is closed, its state is dsInactive, meaning that nothing can be done to its data. At run time you can examine a dataset's read-only State property to determine its current state. The following table summarizes possible values for the State property and what they mean:

Value	State	Meaning
dsInactive	Inactive	Dataset closed. Its data is unavailable.
dsBrowse	Browse	Dataset open. Its data can be viewed, but not changed. This is the default state of an open dataset.
dsEdit	Edit	Dataset open. The current row can be modified.
dsInsert	Insert	Dataset open. A new row is inserted or appended.
dsSetKey	SetKey	TTable and TClientDataSet only. Dataset open. Enables setting of ranges and key values used for ranges and GotoKey operations.
dsCalcFields	CalcFields	Dataset open. Indicates that an OnCalcFields event is under way. Prevents changes to fields that are not calculated.
dsCurValue	CurValue	Internal use only.
dsNewValue	NewValue	Internal use only.
dsOldValue	OldValue	Internal use only.
dsFilter	Filter	Dataset open. Indicates that a filter operation is under way. A restricted set of data can be viewed, and no data can be changed.

When an application opens a dataset, Delphi automatically puts the dataset into dsBrowse mode. The state of a dataset changes as an application processes data. An open dataset changes from one state to another based on either the

Code in your application, or

Delphi's built-in behavior.

To put a dataset into dsBrowse, dsEdit, dsInsert, or dsSetKey states, call the method corresponding to the name of the state. For example, the following code puts CustTable into dsInsert state, accepts user input for a new record, and writes the new record to the database:

CustTable.Insert; { Your application explicitly sets dataset state to Insert }

AddressPromptDialog.ShowModal;

if AddressPromptDialog.ModalResult := mrOK then

 CustTable.Post; { Delphi sets dataset state to Browse on successful completion }

else

 CustTable.Cancel; {Delphi sets dataset state to Browse on cancel }

This example also illustrates that Delphi automatically sets the state of a dataset to dsBrowse when

The Post method successfully writes a record to the database. (If Post fails, the dataset state remains unchanged.)

The Cancel method is called.

Some states cannot be set directly. For example, to put a dataset into dsInactive state, set its Active property to False, or call the Close method for the dataset. The following statements are equivalent:

CustTable.Active := False;

CustTable.Close;

The remaining states (dsCalcFields, dsCurValue, dsNewValue, dsOldValue, and dsFilter) cannot be set by your application. Instead, Delphi sets them as necessary. For example, dsCalcFields is set when a dataset's OnCalcFields event is called. When the OnCalcFields event finishes, the dataset is restored to its previous state.

Note:	Whenever a dataset's state changes, the OnStateChange event is called for any data source components associated with the dataset. For more information about data source components and OnStateChange, see "Using TDataSource events."

The following topics provide overviews of each state, how and when states are set, how states relate to one another, and where to go for related information, if applicable.


Inactivating a dataset

Section topics 	See also

A dataset is inactive when it is closed. You cannot access records in a closed dataset. At design time, a dataset is closed until you set its Active property to True. At run time, a dataset is initially closed until an application opens it by calling the Open method, or by setting the Active property to True.

When you open an inactive dataset, Delphi automatically puts it into dsBrowse state. The following diagram illustrates the relationship between these states and the methods that set them.

Relationship of Inactive and Browse states

To make a dataset inactive, call its Close method. You can write BeforeClose and AfterClose event handlers that respond to the Close method for a dataset. For example, if a dataset is in dsEdit or dsInsert modes when an application calls Close, you should prompt the user to post pending changes or cancel them before closing the dataset. The following code illustrates such a handler:

procedure CustTable.VerifyBeforeClose(DataSet: TDataSet)

begin

 if (CustTable.State = dsEdit) or (CustTable.State = dsInsert) then

 begin

   if MessageDlg('Post changes before closing?', mtConfirmation, mbYesNo, 0) = mrYes then

     CustTable.Post;

   else

     CustTable.Cancel;

 end;

end;

To associate a procedure with the BeforeClose event for a dataset at design time:

1.	Select the table in the data module (or form).

2.	Click the Events page in the Object Inspector.

3.	Enter the name of the procedure for the BeforeClose event (or choose it from the drop-down list).


Browsing a dataset

Section topics 	See also

When an application opens a dataset, Delphi automatically puts the dataset into dsBrowse state. Browsing enables you to view records in a dataset, but you cannot edit records or insert new records. You mainly use dsBrowse to scroll from record to record in a dataset. For more information about scrolling from record to record, see "Navigating datasets."

From dsBrowse all other dataset states can be set. For example, calling the Insert or Append methods for a dataset changes its state from dsBrowse to dsInsert (note that other factors and dataset properties such as CanModify, may prevent this change). Calling SetKey to search for records puts a dataset in dsSetKey mode. For more information about inserting and appending records in a dataset, see "Modifying data."

Two methods associated with all datasets can return a dataset to dsBrowse state. Cancel ends the current edit, insert, or search task, and always returns a dataset to dsBrowse state. Post attempts to write changes to the database, and if successful, also returns a dataset to dsBrowse state. If Post fails, the current state remains unchanged.

The following diagram illustrates the relationship of dsBrowse both to the other dataset modes you can set in your applications, and the methods that set those modes.

Relationship of Browse to other dataset states


Enabling dataset editing

Section topics 	See also

A dataset must be in dsEdit mode before an application can modify records. In your code you can use the Edit method to put a dataset into dsEdit mode if the read-only CanModify property for the dataset is True. CanModify is True if the database underlying a dataset permits read and write privileges.

On forms in your application, some data-aware controls can automatically put a dataset into dsEdit state if:

The control's ReadOnly property is False (the default),

The AutoEdit property of the data source for the control is True, and

CanModify is True for the dataset.

Important:	For TTable components, if the ReadOnly property is True, CanModify is False, preventing editing of records. Similarly, for TQuery components, if the RequestLive property is False, CanModify is False.

Note:	Even if a dataset is in dsEdit state, editing records may not succeed for SQL-based databases if your application user does not have proper SQL access privileges.

You can return a dataset from dsEdit state to dsBrowse state in code by calling the Cancel, Post, or Delete methods. Cancel discards edits to the current field or record. Post attempts to write a modified record to the dataset, and if it succeeds, returns the dataset to dsBrowse. If Post cannot write changes, the dataset remains in dsEdit state. Delete attempts to remove the current record from the dataset, and if it succeeds, returns the dataset to dsBrowse state. If Delete fails, the dataset remains in dsEdit state.

Data-aware controls for which editing is enabled automatically call Post when a user executes any action that changes the current record (such as moving to a different record in a grid) or that causes the control to lose focus (such as moving to a different control on the form).

For a complete discussion of editing fields and records in a dataset, see "Modifying data."


Enabling insertion of new records

Section topics 	See also

A dataset must be in dsInsert mode before an application can add new records. In your code you can use the Insert or Append methods to put a dataset into dsInsert mode if the read-only CanModify property for the dataset is True. CanModify is True if the database underlying a dataset permits read and write privileges.

On forms in your application, the data-aware grid and navigator controls can put a dataset into dsInsert state if

The control's ReadOnly property is False (the default),

The AutoEdit property of the data source for the control is True, and

CanModify is True for the dataset.

Important:	For TTable components, if the ReadOnly property is True, CanModify is False, preventing editing of records. Similarly, for TQuery components, if the RequestLive property is False, CanModify is False.

Note:	Even if a dataset is in dsInsert state, inserting records may not succeed for SQL-based databases if your application user does not have proper SQL access privileges.

You can return a dataset from dsInsert state to dsBrowse state in code by calling the Cancel, Post, or Delete methods. Delete and Cancel discard the new record. Post attempts to write the new record to the dataset, and if it succeeds, returns the dataset to dsBrowse. If Post cannot write the record, the dataset remains in dsInsert state.

Data-aware controls for which inserting is enabled automatically call Post when a user executes any action that changes the current record (such as moving to a different record in a grid).

For more discussion of inserting and appending records in a dataset, see "Modifying data."


Enabling index-based searches and ranges on tables

Section topics 	See also

You can search against any dataset using the Locate and Lookup methods of TDataSet. TTable components, however, provide an additional family of GotoKey and FindKey methods that enable you to search for records based on an index for the table. To use these methods on table components, the component must be in dsSetKey mode. dsSetKey mode applies only to TTable components. You put a dataset into dsSetKey mode with the SetKey method at run time. The GotoKey, GotoNearest, FindKey, and FindNearest methods, which carry out searches, returns the dataset to dsBrowse state upon completion of the search. For more information about searching a table based on its index, see "Working with tables."

You can temporarily view and edit a subset of data for any dataset by using filters. For more information about filters, see "Displaying and editing a subset of data using filters." TTable components also support and additional way to access a subset of available records, called ranges. To create and apply a range to a table, a table must be in dsSetKey mode. For more information about using ranges, see "Working with tables.".


Calculating fields

Section topics 	See also

Delphi puts a dataset into dsCalcFields mode whenever an application calls the dataset's OnCalcFields event handler. This state prevents modifications or additions to the records in a dataset except for the calculated fields the handler is designed to modify. The reason all other modifications are prevented is because OnCalcFields uses the values in other fields to derive values for calculated fields. Changes to those other fields might otherwise invalidate the values assigned to calculated fields.

When the OnCalcFields handler finishes, the dataset is returned to dsBrowse state.

For more information about creating calculated fields and OnCalcFields event handlers, see "Using OnCalcFields."


Filtering records

Section topics 	See also

Delphi puts a dataset into dsFilter mode whenever an application calls the dataset's OnFilterRecord event handler. This state prevents modifications or additions to the records in a dataset during the filtering process so that the filter request is not invalidated. For more information about filtering, see "Displaying and editing a subset of data using filters."

When the OnFilterRecord handler finishes, the dataset is returned to dsBrowse state.


Updating records

Section topics 	See also

When performing cached update operations, Delphi may put the dataset into dsNewValue, dsOldValue, or dsCurValue states temporarily. These state indicate that the corresponding properties of a field component (NewValue, OldValue, and CurValue, respectively) are being accessed, usually in an OnUpdateError event handler. Your applications cannot see or set these states. For more information about using cached updates, see "Working with cached updates."


Navigating datasets

Section topics 	See also

Each active dataset has a cursor, or pointer, to the current row in the dataset. The current row in a dataset is the one whose values can be manipulated by edit, insert, and delete methods, and the one whose field values currently show in single-field, data-aware controls on a form, such as TDBEdit, TDBLabel, and TDBMemo.

You can change the current row by moving the cursor to point at a different row. The following table lists methods you can use in application code to move to different records:

Method	Description
First	Moves the cursor to the first row in a dataset.
Last	Moves the cursor to the last row in a dataset.
Next	Moves the cursor to the next row in a dataset.
Prior	Moves the cursor to the previous row in a dataset.
MoveBy	Moves the cursor a specified number of rows forward or back in a dataset.

The data-aware, visual component TDBNavigator encapsulates these methods as buttons that users can click to move among records at run time. For more information about the navigator component, see "Using data controls."

In addition to these methods, the following table describes two Boolean properties of datasets that provide useful information when iterating through the records in a dataset.

Property	Description
BOF (Beginning-of-file)	True: the cursor is at the first row in the dataset.False: the cursor is not known to be at the first row in the dataset.
EOF (End-of-file)	True: the cursor is at the last row in the dataset.False: the cursor is not known to be at the last row in the dataset.


Using the First and Last methods

Section topics 	See also

The First method moves the cursor to the first row in a dataset and sets the BOF property to True. If the cursor is already at the first row in the dataset, First does nothing.

For example, the following code moves to the first record in CustTable:

CustTable.First;

The Last method moves the cursor to the last row in a dataset and sets the EOF property to True. If the cursor is already at the last row in the dataset, Last does nothing.

The following code moves to the last record in CustTable:

CustTable.Last;

Note:	While there may be programmatic reasons to move to the first or last rows in a dataset without user intervention, you should enable your users to navigate from record to record using the TDBNavigator component. The navigator component contains buttons that when active and visible enables a user to move to the first and last rows of an active dataset. The OnClick events for these buttons call the First and Last methods of the dataset. For more information about making effective use of the navigator component, see "Using data controls."


Using the Next and Prior methods

Section topics 	See also

The Next method moves the cursor forward one row in the dataset and sets the BOF property to False if the dataset is not empty. If the cursor is already at the last row in the dataset when you call Next, nothing happens.

For example, the following code moves to the next record in CustTable:

CustTable.Next;

The Prior method moves the cursor back one row in the dataset, and sets EOF to False if the dataset is not empty. If the cursor is already at the first row in the dataset when you call Prior, Prior does nothing.

For example, the following code moves to the previous record in CustTable:

CustTable.Prior;


Using the MoveBy method

Section topics 	See also

MoveBy enables you to specify how many rows forward or back to move the cursor in a dataset. Movement is relative to the current record at the time that MoveBy is called. MoveBy also sets the BOF and EOF properties for the dataset as appropriate.

This function takes an integer parameter, the number of records to move. Positive integers indicate a forward move and negative integers indicate a backward move.

MoveBy returns the number of rows it moves. If you attempt to move past the beginning or end of the dataset, the number of rows returned by MoveBy differs from the number of rows you requested to move. This is because MoveBy stops when it reaches the first or last record in the dataset.

The following code moves two records backward in CustTable:

CustTable.MoveBy(-2);

Note:	If you use MoveBy in your application and you work in a multi-user database environment, keep in mind that datasets are fluid. A record that was five records back a moment ago may now be four, six, or even an unknown number of records back because several users may be simultaneously accessing the database and changing its data.


Using the EOF and BOF properties

Section topics 	See also

Two read-only, run-time properties, EOF (End-of-file) and BOF (Beginning-of-file), are useful for controlling dataset navigation, particularly when you want to iterate through all records in a dataset.

EOF

When EOF is True, it indicates that the cursor is unequivocally at the last row in a dataset. EOF is set to True when an application

Opens an empty dataset.

Calls a dataset's Last method.

Calls a dataset's Next method, and the method fails (because the cursor is currently at the last row in the dataset.

Calls SetRange on an empty range or dataset.

EOF is set to False in all other cases; you should assume EOF is False unless one of the conditions above is met and you test the property directly.

EOF is commonly tested in a loop condition to control iterative processing of all records in a dataset. If you open a dataset containing records (or you call First) EOF is False. To iterate through the dataset a record at a time, create a loop that terminates when EOF is True. Inside the loop, call Next for each record in the dataset. EOF remains False until you call Next when the cursor is already on the last record.

The following code illustrates one way you might code a record-processing loop for a dataset called CustTable:

CustTable.DisableControls;

try

 CustTable.First; { Go to first record, which sets EOF False }

 while not CustTable.EOF do { Cycle until EOF is True }

 begin

   { Process each record here }

   ...

   CustTable.Next; { EOF False on success; EOF True when Next fails on last record }

 end;

finally

 CustTable.EnableControls;

end;

Tip:	This example also demonstrates how to disable and enable data-aware visual controls tied to a dataset. If you disable visual controls during dataset iteration, it speeds processing because Delphi does not have to update the contents of the controls as the current record changes.After iteration is complete, controls should be enabled again to update them with values for the new current row. Note that enabling of the visual controls takes place in the finally clause of a try...finally statement. This guarantees that even if an exception terminates loop processing prematurely, controls are not left disabled.

BOF

When BOF is True, it indicates that the cursor is unequivocally at the first row in a dataset. BOF is set to True when an application

Opens a dataset.

Calls a dataset's First method.

Calls a dataset's Prior method, and the method fails (because the cursor is currently at the first row in the dataset.

Calls SetRange on an empty range or dataset.

BOF is set to False in all other cases; you should assume BOF is False unless one of the conditions above is met and you test the property directly.

Like EOF, BOF can be in a loop condition to control iterative processing of records in a dataset. The following code illustrates one way you might code a record-processing loop for a dataset called CustTable:

CustTable.DisableControls; { Speed up processing; prevent screen flicker }

try

 while not CustTable.BOF do { Cycle until BOF is True }

 begin

   { Process each record here }

   ...

   CustTable.Prior; { BOF False on success; BOF True when Prior fails on first record }

 end;

finally

 CustTable.EnableControls; { Display new current row in controls }

end;


Marking and returning to records

Section topics 	See also

In addition to moving from record to record in a dataset (or moving from one record to another by a specific number of records), it is often also useful to mark a particular location in a dataset so that you can return to it quickly when desired. TDataSet and its descendants implement a bookmarking feature that enables you to tag records and return to them later. The bookmarking feature consists of a Bookmark property and five bookmark methods.

The Bookmark property indicates which bookmark among any number of bookmarks in your application is current. Bookmark is a string that identifies the current bookmark. Each time you add another bookmark, it becomes the current bookmark.

TDataSet implements virtual bookmark methods. While these methods ensure that any dataset object derived from TDataSet returns a value if a bookmark method is called, the return values are merely defaults. Descendants of TDataSet, such as TBDEDataSet, reimplement the bookmark methods to return meaningful values as described in the following list:

BookmarkValid, for determining if a specified bookmark is in use.

CompareBookmarks, to test two bookmarks to see if they are the same.

GetBookmark, to allocate a bookmark for your current position in the dataset.

GotoBookmark, to return to a bookmark previously created by GetBookmark

FreeBookmark, to free a bookmark previously allocated by GetBookmark.

To create a bookmark, you must declare a variable of type TBookmark in your application, then call GetBookmark to allocate storage for the variable and set its value to a particular location in a dataset. The TBookmark variable is a pointer.

Before calling GotoBookmark to move to a specific record, you can call BookmarkValid to determine if the bookmark points to a record. BookmarkValid returns True if a specified bookmark points to a record. In TDataSet, BookmarkValid is a virtual method that always returns False, indicating that the bookmark is not valid. TDataSet descendants reimplement this method to provide a meaningful return value.

You can also call CompareBookmarks to see if a bookmark you want to move to is different from another (or the current) bookmark. TDataSet.CompareBookmark always returns 0, indicating that the bookmarks are not identical. TDataSet descendants reimplement this method to provide a meaningful return value.

When passed a bookmark, GotoBookmark moves the cursor for the dataset to the location specified in the bookmark. TDataSet.GotoBookmark calls an abstract, internal method which generates a run-time error if called. TDataSet descendants reimplement this method to provide a meaningful return value.

FreeBookmark frees the memory allocated for a specified bookmark when you no longer need it. You should also call FreeBookmark before reusing an existing bookmark.

The following code illustrates one use of bookmarking:

procedure DoSomething (const Tbl: TTable)

var

 Bookmark: TBookmark;

begin

 Bookmark := Tbl.GetBookmark; { Allocate memory and assign a value }

 Tbl.DisableControls; { Turn off display of records in data controls }

 try

   Tbl.First; { Go to first record in table }

   while not Tbl.EOF do {Iterate through each record in table }

   begin

     { Do your processing here }

     ...

     Tbl.Next;

   end;

 finally

   Tbl.GotoBookmark(Bookmark);

   Tbl.EnableControls; { Turn on display of records in data controls, if necessary }

   Tbl.FreeBookmark(Bookmark); {Deallocate memory for the bookmark }

 end;

end;

Before iterating through records, controls are disabled. Should an error occur during iteration through records, the finally clause ensures that controls are always enabled and that the bookmark is always freed even if the loop terminates prematurely.


Searching datasets

Section topics 	See also

You can search any dataset for specific records using the generic search methods Locate and Lookup. These methods enable you to search on any type of columns in any dataset.

Using Locate

Locate moves the cursor to the first row matching a specified set of search criteria. In its simplest form, you pass Locate the name of a column to search, a field value to match, and an options flag specifying whether the search is case-insensitive or if it can use partial-key matching. For example, the following code moves the cursor to the first row in the CustTable where the value in the Company column is "Professional Divers, Ltd.":

var

 LocateSuccess: Boolean;

 SearchOptions: TLocateOptions;

begin

 SearchOptions := [loPartialKey];

 LocateSuccess := CustTable.Locate('Company', 'Professional Divers, Ltd.',
    SearchOptions);

end;

If Locate finds a match, the first record containing the match becomes the current record. Locate returns True if it finds a matching record, False if it does not. If a search fails, the current record does not change.

The real power of Locate comes into play when you want to search on multiple columns and specify multiple values to search for. Search values are variants, which enables you to specify different data types in your search criteria. To specify multiple columns in a search string, separate individual items in the string with semicolons.

Because search values are variants, if you pass multiple values, you must either pass a variant array type as an argument (for example, the return values from the Lookup method), or you must construct the variant array on the fly using the VarArrayOf function. The following code illustrates a search on multiple columns using multiple search values and partial-key matching:

with CustTable do

 Locate('Company;Contact;Phone', VarArrayOf(['Sight Diver','P']), loPartialKey);

Locate uses the fastest possible method to locate matching records. If the columns to search are indexed and the index is compatible with the search options you specify, Locate uses the index. For more information about filters, see "Displaying and editing a subset of data using filters."

Using Lookup

Lookup searches for the first row that matches specified search criteria. If it finds a matching row, it forces the recalculation of any calculated fields and lookup fields associated with the dataset, then returns one or more fields from the matching row. Lookup does not move the cursor to the matching row; it only returns values from it.

In its simplest form, you pass Lookup the name of column to search, the field value to match, and the field or fields to return. For example, the following code looks for the first row in the CustTable where the value in the Company column is "Professional Divers, Ltd.", and returns the company name, a contact person, and a phone number for the company:

var

 LookupResults: Variant;

begin

with CustTable do

 LookupResults := Lookup('Company', 'Professional Divers, Ltd.', 'Company;
    Contact; Phone');

end;

Lookup returns values for the specified fields from the first matching record it finds. Values are returned as variants. If more than one return value is requested, Lookup returns a variant array. If there are no matching records, Lookup returns a Null variant. For more information about variant arrays, see the Object Pascal Language Guide.

The real power of Lookup comes into play when you want to search on multiple columns and specify multiple values to search for. To specify strings containing multiple columns or result fields, separate individual fields in the string items with semi-colons.

Because search values are variants, if you pass multiple values, you must either pass a variant array type as an argument (for example, the return values from the Lookup method), or you must construct the variant array on the fly using the VarArrayOf function. The following code illustrates a lookup search on multiple columns:

var

 LookupResults: Variant;

begin

with CustTable do

 LookupResults := Lookup('Company; City', VarArrayOf(['Sight Diver', 'Christiansted']),
    'Company; Addr1; Addr2; State; Zip');

end;

Lookup uses the fastest possible method to locate matching records. If the columns to search are indexed, Lookup uses the index. For more information about filters, see "Displaying and editing a subset of data using filters."


Displaying and editing a subset of data using filters

Section topics 	See also

An application is frequently interested in only a subset of records within a dataset. For example, you may be interested in retrieving or viewing only those records for companies based in California in your customer database, or you may want to find a record that contains a particular set of field values. In each case, you can use filters to restrict an application's access to a subset of all records in the dataset.

A filter specifies conditions a record must meet in order to be displayed. Filter conditions can be stipulated in a dataset's Filter property or coded into its OnFilterRecord event handler. Filter conditions are based on the values in any specified number of fields in a dataset whether or not those fields are indexed. For example, to view only those records for companies based in California, a simple filter might require that records contain a value in the State field of 'CA'.

Note:	Filters are applied to every record retrieved in a dataset. When you want to filter large volumes of data, it may be more efficient to use a query to restrict record retrieval, or to set a range on an indexed table rather than using filters.


Enabling and disabling filtering

Section topics 	See also

Enabling filters on a dataset is a three-step process:

1.	Create a filter.

2.	Set filter options for string-based filter tests, if necessary.

3.	Set the Filtered property to True.

When filtering is enabled, only those records that meet the filter criteria are available to an application. Filtering is always a temporary condition. You can turn off filtering by setting the Filtered property to False.


Creating filters

Section topics 	See also

There are two ways to create a filter for a dataset:

Specify simple filter conditions in the Filter property. Filter is especially useful for creating and applying filters at run time.

Write an OnFilterRecord event handler for simple or complex filter conditions. With OnFilterRecord, you specify filter conditions at design time. Unlike the Filter property, which is restricted to a single string containing filter logic, an OnFilterRecord event can take advantage of branching and looping logic to create complex, multi-level filter conditions.

The main advantage to creating filters using the Filters property is that your application can create, change, and apply filters dynamically, (for example, in response to user input). Its main disadvantages are that filter conditions must be expressible in a single text string, cannot make use of branching and looping constructs, and cannot test or compare its values against values not already in the dataset.

The strengths of the OnFilterRecord event are that a filter can be complex and variable, can be based on multiple lines of code that use branching and looping constructs, and can test dataset values against values outside the dataset, such as the text in an edit box. The main weakness of using OnFilterRecord is that you set the filter at design time and it cannot be modified in response to user input. (You can, however, create several filter handlers and switch among them in response to general application conditions.)

The following topics describe how to create filters using the Filter property and the OnFilterRecord event handler.


Setting the Filter property

Section topics 	See also

To create a filter using the Filter property, set the value of the property to a string that contains the filter conditions. The string contains the filter's test condition. For example, the following statement creates a filter that tests a dataset's State field to see if it contain a value for the state of California:

Dataset1.Filter := 'State' = 'CA';

You can also supply a value for Filter based on the text entered in a control. For example, the following statement assigns the text in an edit box to Filter:

Dataset1.Filter := Edit1.Text;

You can, of course, create a string based on both hard-coded text and data entered by a user in a control:

Dataset1.Filter := 'State' + ' ' + Edit1.Text;

After you specify a value for Filter, to apply the filter to the dataset, set the Filtered property to True.

You can also compare field values to literals, and to constants using the following comparison and logical operators:

Operator	Meaning
<	Less than
>	Greater than
>=	Greater than or equal to
<=	Less than or equal to
=	Equal to
<>	Not equal to
AND	Tests two statements are both True
NOT	Tests that the following statement is not True
OR	Tests that at least one of two statements is True

By using combinations of these operators, you can create fairly sophisticated filters. For example, the following statement checks to make sure that two test conditions are met before accepting a record for display:

(Custno > 1400) AND (Custno < 1500);

Note:	When filtering is on, user edits to a record may mean that the record no longer meets
a filter's test conditions. The next time the record is retrieved from the dataset, it may therefore "disappear." If that happens, the next record that passes the filter condition becomes the current record.


Writing an OnFilterRecord event handler

Section topics 	See also

A filter for a dataset is an event handler that responds to OnFilterRecord events generated by the dataset for each record it retrieves. At the heart of every filter handler is a test that determines if a record should be included in those that are visible to the application.

To indicate whether a record passes the filter condition, your filter handler must set an Accept parameter to True to include a record, or False to exclude it. For example, the following filter displays only those records with the State field set to "CA":

procedure TForm1.Table1FilterRecord(DataSet: TDataSet; var Accept: Boolean);

begin

 Accept := DataSet['State'] = 'CA';

end;

When filtering is enabled, an OnFilterRecord event is generated for each record retrieved. The event handler tests each record, and only those that meet the filter's conditions are displayed. Because the OnFilterRecord event is generated for every record in a dataset, you should keep the event handler as tightly-coded as possible to avoid adversely affecting the performance of your application.

Switching filter event handlers at run time

You can code any number of filter event handlers and switch among them at run time. To switch to a different filter event handler at run time, assign the new event handler to the dataset's OnFilterRecord property.

For example, the following statements switch to an OnFilterRecord event handler called NewYorkFilter:

DataSet1.OnFilterRecord := NewYorkFilter;


Setting filter options

Section topics 	See also

The FilterOptions property enables you to specify whether or not a filter that compares string-based fields accepts records based on partial comparisons and whether or not string comparisons are case-sensitive. FilterOptions is a set property that can be an empty set (the default), or that can contain either or both of the following values:

Value	Meaning
foCaseInsensitive	Ignore case when comparing strings.
foPartialCompare	Disable partial string matching (i.e., do not match strings ending with an asterisk (*)).

For example, the following statements set up a filter that ignores case when comparing values in the State field:

FilterOptions := [foCaseInsensitive];

Filter := 'State' = 'CA';


Navigate records in a filtered dataset

Section topics 	See also

There are four dataset methods that enable you to navigate among records in a filtered dataset. The following table lists these methods and describes what they do:

Method	Purpose
FindFirst	Move to the first record in the dataset that matches the current filter criteria. The search for the first matching record always begins at the first record in the unfiltered dataset.
FindLast	Move to the last record in the dataset that matches the current filter criteria.
FindNext	Moves from the current record in the filtered dataset to the next one.
FindPrior	Move from the current record in the filtered dataset to the previous one.

For example, the following statement finds the first filtered record in a dataset:

DataSet1.FindFirst;

Provided that you set the Filter property or create an OnFilterRecord event handler for your application, these methods position the cursor on the specified record whether or not filtering is currently enabled for the dataset. If you call these methods when filtering is not enabled, then they

1.	Temporarily enable filtering.

2.	Position the cursor on a matching record if one is found.

3.	Disable filtering.

Note:	If filtering is disabled and you do not set the Filter property or create an OnFilterRecord event handler, these methods do nothing.

All navigational filter methods position the cursor on a matching record (if one if found) make that record the current one, and return True. If a matching record is not found, the cursor position is unchanged, and these methods return False. You can check the status of the Found property to wrap these calls, and take action only when Found is True. For example, if the cursor is already on the last matching record in the dataset, and you call FindNext, the method returns False, and the current record is unchanged.


Modifying data

Section topics 	See also

You can use the following dataset methods to insert, update, and delete data:

Method	Description
Edit	Puts the dataset into dsEdit state if it is not already in dsEdit or dsInsert states.
Append	Posts any pending data, moves current record to the end of the dataset, and puts the dataset in dsInsert state.
Insert	Posts any pending data, and puts the dataset in dsInsert state.
Post	Attempts to post the new or altered record to the database. If successful, the dataset is put in dsBrowse state; if unsuccessful, the dataset remains in its current state.
Cancel	Cancels the current operation and puts the dataset in dsBrowse state.
Delete	Deletes the current record and puts the dataset in dsBrowse state.


Editing records

Section topics 	See also

A dataset must be in dsEdit mode before an application can modify records. In your code you can use the Edit method to put a dataset into dsEdit mode if the read-only CanModify property for the dataset is True. CanModify is True if the table(s) underlying a dataset permits read and write privileges.

On forms in your application, some data-aware controls can automatically put a dataset into dsEdit state if

The control's ReadOnly property is False (the default),

The AutoEdit property of the data source for the control is True, and

CanModify is True for the dataset.

Important:	For TTable components (and TQuery when the RequestLive property is False), if the ReadOnly property is True, CanModify is False, preventing editing of records.

Note:	Even if a dataset is in dsEdit state, editing records may not succeed for SQL-based databases if your application's user does not have proper SQL access privileges.

Once a dataset is in dsEdit mode, a user can modify the field values for the current record that appears in any data-aware controls on a form. Data-aware controls for which editing is enabled automatically call Post when a user executes any action that changes the current record (such as moving to a different record in a grid).

If you provide a navigator component on your forms, users can cancel edits by clicking the navigator's Cancel button. Cancelling edits returns a dataset to dsBrowse state.

In code, you must write or cancel edits by calling the appropriate methods. You write changes by calling Post. You cancel them by calling Cancel. In code, Edit and Post are often used together. For example,

with CustTable do

begin

 Edit;

 FieldValues['CustNo'] := 1234;

 Post;

end;

Inside the begin...end statement, the first line of code places the dataset in dsEdit mode. The next line of code assigns the number 1234 to the CustNo field of the current record. Finally, the last line writes, or posts, the modified record to the database.

Note:	If the CachedUpdates property for a dataset is True, posted modifications are written to a temporary buffer. To write cached edits to the database, call the ApplyUpdates method for the dataset. For more information about cached updates, see "Working with cached updates."


Adding new records

Section topics 	See also

A dataset must be in dsInsert mode before an application can add new records. In code, you can use the Insert or Append methods to put a dataset into dsInsert mode if the read-only CanModify property for the dataset is True. CanModify is True if the database underlying a dataset permits read and write privileges.

On forms in your application, the data-aware grid and navigator controls can put a dataset into dsInsert state if

The control's ReadOnly property is False (the default), and

CanModify is True for the dataset.

Once a dataset is in dsInsert mode, a user or application can enter values into the fields associated with the new record. Except for the grid and navigational controls, there is no visible difference to a user between Insert and Append. On a call to Insert, an empty row appears in a grid above what was the current record. On a call to Append, the grid is scrolled to the last record in the dataset, an empty row appears at the bottom of the grid, and the Next and Last buttons are dimmed on any navigator component associated with the dataset.

Data-aware controls for which inserting is enabled automatically call Post when a user executes any action that changes which record is current (such as moving to a different record in a grid). Otherwise you must call Post in your code.

Post writes the new record to the database, or, if cached updates are enabled, Post writes the record to a buffer. To write cached inserts and appends to the database, call the ApplyUpdates method for the dataset. For more information about cached updates, see "Working with cached updates."

Inserting records

Insert opens a new, empty record before the current record, and makes the empty record the current record so that field values for the record can be entered either by a user or by your application code.

When an application calls Post (or ApplyUpdates when cached updating is enabled), a newly inserted record is written to a database in one of three ways:

For indexed Paradox and dBASE tables, the record is inserted into the dataset in a position based on its index.

For unindexed tables, the record is inserted into the dataset at its current position.

For SQL databases, the physical location of the insertion is implementation-specific. If the table is indexed, the index is updated with the new record information.

Appending records

Append opens a new, empty record at the end of the dataset, and makes the empty record the current one so that field values for the record can be entered either by a user or by your application code.

When an application calls Post (or ApplyUpdates when cached updating is enabled), a newly appended record is written to a database in one of three ways:

For indexed Paradox and dBASE tables, the record is inserted into the dataset in a position based on its index.

For unindexed tables, the record is added to the end of the dataset.

For SQL databases, the physical location of the append is implementation-specific. If the table is indexed, the index is updated with the new record information.


Deleting records

Section topics 	See also

A dataset must be active before an application can delete records. Delete deletes the current record from a dataset and puts the dataset in dsBrowse mode. The record that followed the deleted record becomes the current record. If cached updates are enabled for a dataset, a deleted record is removed from the temporary cache buffer only until you call ApplyUpdates.

If you provide a navigator component on your forms, users can delete the current record by clicking the navigator's Delete button. In code, you must call Delete explicitly to remove the current record.


Posting data to the database

Section topics 	See also

The Post method is central to a Delphi application's interaction with a database table. Post writes changes to the current record to the database, but it behaves differently depending on a dataset's state.

In dsEdit state, Post writes a modified record to the database (or buffer if cached updates is enabled).

In dsInsert state, Post writes a new record to the database (or buffer if cached updates is enabled).

In dsSetKey state, Post returns the dataset to dsBrowse state.

Posting can be done explicitly, or implicitly as part of another procedure. When an application moves off the current record, Delphi calls Post implicitly. Calls to the First, Next, Prior, and Last methods perform a Post if the table is in dsEdit or dsInsert modes. The Append and Insert methods also implicitly post any pending data.

Note:	Post is not called implicitly by the Close method. Use the BeforeClose event to post any pending edits explicitly.


Canceling changes

Section topics 	See also

An application can undo changes made to the current record at any time, if it has not yet directly or indirectly called Post. For example, if a dataset is in dsEdit mode, and a user has changed the data in one or more fields, the application can return the record back to its original values by calling the Cancel method for the dataset. A call to Cancel always returns a dataset to dsBrowse state.

On forms, you can allow users to cancel edit, insert, or append operations by including the Cancel button on a navigator component associated with the dataset, or you can provide code for your own Cancel button on the form.


Modifying entire records

Section topics 	See also

On forms, all data-aware controls except for grids and the navigator provide access to a single field in a record.

In code, however, you can use the following methods that work with entire record structures provided that the structure of the database tables underlying the dataset is stable and does not change. The following table summarizes the methods available for working with entire records rather than individual fields in those records:

Method	Description
AppendRecord([array of values])	Appends a record with the specified column values at the end of a table; analogous to Append. Performs an implicit Post.
InsertRecord([array of values])	Inserts the specified values as a record before the current cursor position of a table; analogous to Insert. Performs an implicit Post.
SetFields([array of values])	Sets the values of the corresponding fields; analogous to assigning values to TFields. Application must perform an explicit Post.

These method take a comma-delimited array of values as an argument, where each value corresponds to a column in the underlying dataset. The values can be literals, variables, NULL, or nil. If the number of values in an argument is less than the number of columns in a dataset, then the remaining values are assumed to be NULL.

For unindexed datasets, AppendRecord adds a record to the end of the dataset and InsertRecord inserts a record after the current cursor position. For indexed tables, both methods places the record in the correct position in the table, based on the index. In both cases, the methods move the cursor to the record's position.

SetFields assigns the values specified in the array of parameters to fields in the dataset. To use SetFields, an application must first call Edit to put the dataset in dsEdit mode. To apply the changes to the current record, it must perform a Post.

If you use SetFields to modify some, but not all fields in an existing record, you can pass nil values for fields you do not want to change. If you do not supply enough values for all fields in a record, SetFields assigns NULL values to them. NULL values overwrite any existing values already in those fields.

For example, suppose a database has a COUNTRY table with columns for Name, Capital, Continent, Area, and Population. If a TTable component called CountryTable were linked to the COUNTRY table, the following statement would insert a record into the COUNTRY table:

CountryTable.InsertRecord(['Japan', 'Tokyo', 'Asia']);

This statement does not specify values for Area and Population, so NULL values are inserted for them. The table is indexed on Name, so the statement would insert the record based on the alphabetic collation of "Japan".

To update the record, an application could use the following code:

with CountryTable do

begin

 if Locate('Name', 'Japan', loCaseInsensitive) then;

 begin

   Edit;

   SetFields(nil, nil, nil, 344567, 164700000);

   Post;

 end;

end;

This code assigns values to the Area and Population fields and then posts them to the database. The three nil values act as place holders for the first three columns to preserve their current contents.


Using dataset events

Section topics 	See also

Datasets have a number of events that enable an application to perform validation, compute totals, and perform other tasks. The events are listed in the following table.

Event	Description
BeforeOpen, AfterOpen	Called before/after a dataset is opened.
BeforeClose, AfterClose	Called before/after a dataset is closed.
BeforeInsert, AfterInsert	Called before/after a dataset enters Insert state.
BeforeEdit, AfterEdit	Called before/after a dataset enters Edit state.
BeforePost, AfterPost	Called before/after changes to a table are posted.
BeforeCancel, AfterCancel	Called before/after the previous state is canceled.
BeforeDelete, AfterDelete	Called before/after a record is deleted.
OnNewRecord	Called when a new record is created; used to set default values.
OnCalcFields	Called when calculated fields are calculated.

For more information about events for the DataSet component, see TDataSet.


Aborting a method

Section topics 	See also

To abort a method such as an Open or Insert, call the Abort procedure in any of the Before methods (BeforeOpen, BeforeInsert, and so on). For example, the following code requests a user to confirm a delete operation or else it aborts the call to Delete:

procedure TForm1.TableBeforeDelete (Dataset: TDataset);

begin

 if MessageDlg('Delete This Record?', mtConfirmation, mbYesNoCancel, 0) <> mrYes then

   Abort;

end;


Using OnCalcFields

Section topics 	See also

The OnCalcFields event is used to set the values of calculated fields. The AutoCalcFields property determines when OnCalcFields is called. If AutoCalcFields is True, then OnCalcFields is called when

A dataset is opened.

Focus moves from one visual component to another, or from one column to another in a data-aware grid control and the current record has been modified.

A record is retrieved from the database.

OnCalcFields is always called whenever a value in a non-calculated field changes, regardless of the setting of AutoCalcFields.

Caution:	OnCalcFields is called frequently, so the code you write for it should be kept short. Also, if AutoCalcFields is True, OnCalcFields should not perform any actions that modify the dataset (or the linked dataset if it is part of a master-detail relationship), because this can lead to recursion. For example, if OnCalcFields performs a Post, and AutoCalcFields is True, then OnCalcFields is called again, leading to another Post, and so on.

If AutoCalcFields is False, then OnCalcFields is not called when individual fields within a single record are modified.

When OnCalcFields executes, a dataset is in dsCalcFields mode, so you cannot set the values of any fields other than calculated fields. After OnCalcFields is completed, the dataset returns to dsBrowse state.


See also

TDataSet

TDBEdit

TDBLabel

TDBMemo

TDBEdit

Data Controls components


Understanding BDE-enabled datasets

Section topics 	See also

These topics describe features common to the Delphi dataset components that use the Borland Database Engine (BDE) to access data. Support for BDE-enablement occurs in TBDEDataSet, which is a direct descendant of TDataSet. Additional database and session control features occur in TDBDataSet, which is a direct descendant of TBDEDataSet.

Delphi dataset component hierarchy

These topics specifically introduce the dataset features provided by TBDEDataSet and TDBDataSet. It assumes you are already familiar with TDataSet. For a general understanding of dataset components descended from TDataSet, see "Understanding datasets."

Note:	Although you need to understand the functionality provided by TBDEDataSet and TDBDataSet, unless you develop your own custom BDE-enabled datasets, you never use TBDEDataSet and TDBDataSet directly in your applications. Instead you use the direct descendants of TDBDataSet: TQuery, TStoredProc, and TTable. For specific information about using TStoredProc, see "Working with stored procedures." For specific information about using TQuery, see "Working with queries." For specific information about TTable, see "Working with tables."


Overview of BDE-enablement

Section topics 	See also

The TBDEDataSet component implements the abstract methods of TDataSet that control record navigation, indexing, and bookmarking. It also reimplements many of TDataSet's virtual methods and events to take advantage of the BDE. The BDE-specific implementations of TDataSet's features do not depart from the general description about using these features with TDataSet, so for more information about them, see "Understanding datasets."

In addition to BDE-specific features common to all datasets, TBDEDataSet introduces new properties, events, and methods for handling BLOBs, cached updates, and communicating with a remote database server. TDBDataSet introduces a method and properties for handling database connections and associating a dataset with a BDE session. The following topics describe these features and point to other topics that are also relevant to using them.


Handling database and session connections

Section topics 	See also

The TDBDataSet component introduces the following properties and one function for working with database and session connections:

Function or property	Purpose
CheckOpen function	Determines if a database is open. Returns True if the connection is active, False otherwise.
Database	Identifies the database component with which the dataset is associated.
DBHandle	Specifies the BDE database handle for the database component specified in the Database property. Used only when making some direct BDE API calls.
DBLocale	Specifies the BDE locale information for the database component specified in the Database property. Used only when making some direct BDE API calls.
DBSession	Specifies the BDE session handle for the session component specified by the SessionName property. Used only when making some direct BDE API calls.
DatabaseName	Specifies the BDE alias or database component name for the database used by this dataset. If the dataset is a Paradox or dBASE table, DatabaseName can be a full path specification for the database's directory location.
SessionName	Specifies the session with which this dataset component is associated. If you use both database and session components with a dataset, the setting for SessionName should be the same as the database component's SessionName property.
Using the DatabaseName and SessionName properties

Of these properties, the most commonly used ones are DatabaseName and SessionName. If you work with databases on a remote database server, such as Sybase, Oracle, or InterBase, your application usually maintains that connection through a TDatabase component. You should set the DatabaseName property of each dataset to match the name of the database component that establishes the database connection used by the dataset. If you do not use database components, DatabaseName should be set to a BDE alias (or, optionally, a full path specification for dBASE and Paradox).

SessionName indicates the BDE session with which to associate a dataset. If you do not use explicit session components in your application, you do not have to provide a value for this property. It is supplied for you. If your application provides more than one session, you can set a dataset's SessionName property to match the SessionName property of the appropriate session component in your application. If your application uses both multiple session components and one or more database components, the SessionName property for a dataset must match the SessionName property for the database component with which the dataset is associated.

For more information about handling database connections with TDatabase, see "Connecting to databases." For more information about managing sessions with TSession and TSessionList, see "Managing database sessions."

Working with BDE handle properties

Unless you bypass the built-in functionality of dataset components and make direct API calls to the BDE, you do not need to use the DBHandle, DBLocale, and DBSession properties. These properties are read-only properties that are automatically assigned to a dataset when it is connected to a database server through the BDE. These properties are provided as a resource for application developers who need to make direct API calls to BDE functions, some of which take handle parameters. For more information about the BDE API see the online help file, BDE32.HLP.


Working with a provider component

Section topics 	See also

For dataset components that are part of an application server in a multi-tiered database application, TDBDataSet includes a Provider property. Provider is a read-only property that points to the IProvider interface established by a TProvider component on the application server. When the provider component specifies the dataset on the application server from which it requests data, and to which it sends client application updates, the dataset's Provider property is automatically set to the provider's IProvider interface.

TProvider is only supplied in Delphi Client/Server Suite and Delphi Enterprise. For more information about creating and using provider components in a multi-tiered database application, see "Creating a multi-tiered application."


Working with cached updates

Section topics 	See also

Cached updates enable you to retrieve data from a database, cache and edit it locally, and then apply the cached updates to the database as a unit. When cached updates are enabled, updates to a dataset (such as posting changes or deleting records) are stored in an internal cache instead of being written directly to the dataset's underlying table. When changes are complete, your application calls a method that writes the cached changes to the database and clears the cache.

Implementation of cached updating occurs in TBDEDataSet. The following table lists the properties, events, and methods for cached updating:

Property, event, or method	Purpose
CachedUpdates property	Determines whether or not cached updates are in effect for the dataset. If True, cached updating is enabled. If False, cached updating is disabled.
UpdateObject property	Indicates the name of the TUpdateSQL component used to update datasets based on queries.
UpdatesPending property	Indicates whether or not the local cache contains updated records that need to be applied to the database. True indicates there are records to update. False indicates the cache is empty.
UpdateRecordTypes property	Indicates the kind of updated records to make visible to the application during the application of cached updates.
UpdateStatus property	Indicates if a record is unchanged, modified, inserted, or deleted.
OnUpdateError event	A developer-created procedure that handles update errors on a record-by-record basis.
OnUpdateRecord event	A developer-created procedure that processes updates on a record-by-record basis.
ApplyUpdates method	Applies records in the local cache to the database.
CancelUpdates method	Removes all pending updates from the local cache without applying them to the database.
CommitUpdates method	Clears the update cache following successful application of updates.
FetchAll method	Copies all database records to the local cache for editing and updating.
RevertRecord method	Undoes updates to the current record if updates are not yet applied on the server side.


Caching BLOBs

Section topics 	See also

TBDEDataSet provides the CacheBlobs property to control whether BLOB fields are cached locally by the BDE when an application reads BLOB records. By default, CacheBlobs is True, meaning that the BDE caches a local copy of BLOB fields. Caching BLOBs improves application performance by enabling the BDE to store local copies of BLOBs instead of fetching them repeatedly from the database server as a user scrolls through records.

In applications and environments where BLOBs are frequently updated or replaced, and a fresh view of BLOB data is more important than application performance, you can set CacheBlobs to False to ensure that your application always sees the latest version of a BLOB field.


Responding to a server yield

Section topics 	See also

TBDEDataSet provides an event handler for responding to Sybase server yields that occur during the processing of queries. When a Sybase server periodically returns control to the BDE during query processing, the BDE passes control to a dataset's OnServerYield event handler, if one is provided. Otherwise control is immediately returned to the server. A Sybase server yields control during query processing mainly as a way for applications to cancel or update a query request.


See also

TClientDataSet

TDataSet

TBDEDataSet

TProvider

Understanding datasets

Working with stored procedures

Working with queries

Working with tables

Data Access components


Creating and using a client dataset

Section topics 	See also

Delphi supports different ways of creating database applications. In addition to the traditional one- and two-tiered client/server application pioneered in earlier versions of Delphi, Delphi now supports two more database models using a new component, TClientDataSet:

Single-tiered, in which a standalone application maintains data in a flat file accessed directly from a client dataset component.

Multi-tiered, in which a client application provides a user interface that enables an end user to work with a local copy of data brokered by a remote application server, which, in turn, communicates directly with a database server.

In a single-tiered application, a client dataset component acts as a generic database engine, saving data to, and loading data from a file. A single-tiered application uses a data source component and data-aware controls to provide a user interface to the client dataset.

In a multi-tiered database application, a client application contains a client dataset that operates on a local copy of data requested and received from an application server. The application server is typically on a remote machine. A user interacts with the local copy of data through data-aware controls associated with the client dataset by a data source. In fact, if a client application is correctly designed, users can even take a snapshot of data and work on it off-line, using the "briefcase" model. Eventually, however, the client application must apply the changes from the local copy of data to the application server. The application server applies them to the actual database, and returns errors to the client dataset for handling. For more information about using the briefcase model in a multi-tiered application, see "Creating a multi-tiered application."

These topics describe how to work with the TClientDataSet component in single-tiered and multi-tiered applications. Almost everything that applies to client datasets in a single-tiered application also applies in a multi-tiered application. In multi-tiered applications there are additional considerations. While some of these are discussed here, they are discussed more fully in "Creating a multi-tiered application."

TClientDataSet is a descendant of TDataSet, and the discussion that follows assumes you are familiar with the properties, events, and methods of TDataSet that a client dataset inherits. For more information about TDataSet, see "Understanding datasets."


Working with a client dataset

Section topics 	See also

Place as many client dataset components as necessary in each data module in your application. Linking data-aware controls to client datasets through data source components is done just as it is for any TDataSet descendant.


Specifying a data provider

Section topics 	See also

A client dataset can get its data from any of three sources:

Another client dataset. For more information about getting data from another client dataset, see "Assiging data directly."

A flat file. For more information about retrieving data from a file, see "Loading data from a file."

A data provider object that uses an IProvider interface.

IProvider is an ActiveX dual interface. In both single-tiered applications and in client applications used in the briefcase mode as temporary single-tiered applications, the IProvider interface is not defined. In multi-tiered applications, you typically use the RemoteServer and ProviderName properties to select a provider interface from those available to the client application.

RemoteServer specifies the name of a TMIDASConnection component from which to get a list of providers. The MIDAS connection component resides in the same data module as the client dataset. A MIDAS connection component establishes and maintains a connection to an application server, sometimes called a "data broker". For more information about using the MIDAS connection component in a multi-tiered application, see "Creating a multi-tiered application."

At design time, after you specify RemoteServer, you can select a provider from the drop-down list for the ProviderName property in the Object Inspector. At run time, you can switch among available providers by setting ProviderName in code.


Assiging data directly

Section topics 	See also

Use the client dataset's Data property to assign data to a client dataset when no IProvider interface is available. Data is an OleVariant in the form of a data packet. A data packet can come from another client dataset, or from a provider component. Once the data packet is assigned to Data, its contents are displayed automatically in data-aware controls connected to the client dataset by a data source component.

When you open a client dataset that uses a provider component, data packets are automatically assgined to Data.

For single-tiered applications, data comes from a flat file. In a client application, data can come from a flat file (typical of the briefcase model), or a provider on an application server. When you get data from an application server, you can specify whether or not to retrieve all data at once, or to fetch data in chunks. In either case, the data packet you receive contains the specified data.

 Loading data from a file

To load data from a file, call a client dataset's LoadFromFile procedure. LoadFromFile takes one parameter, a string that specifies the file from which to read data. The file name can be a fully qualified path name, if appropriate.

The data in the file must be data previously saved in a client dataset's data format by this or another client dataset using the SaveToFile procedure. For more information about saving data to a file, see "Saving data to a file."

When you call LoadFromFile, all data in the file is read into the Data property.

Requesting data from an application server

Two properties and one method of TClientDataSet determine how data is fetched from an application server in a multi-tiered application:

Property or method	Purpose
FetchOnDemand property	Determines whether or not a client dataset automatically fetches data as needed, or relies on the application to call the client dataset's GetNextPacket function to retrieve additional data.
PacketRecords property	Specifies the type or number of records to return in each data packet.
GetNextPacket method	Fetches the next data packet from the application server.

By default, a client dataset retrieves all records from the application server. You can control how data is retrieved using PacketRecords and FetchOnDemand.

PacketRecords specifies either how many records to fetch at a time, or the type of records to return. By default, PacketRecords is set to -1, which means that all available records are fetched at once, either when the application is first opened, or the application explicitly calls GetNextPacket. When PacketRecords is -1, then after it first fetches data, a client dataset never needs to fetch more data because it already has all available records.

To fetch records in small batches, set PacketRecords to the number of records to fetch. For example, the following statement sets the size of each data packet to ten records:

ClientDataSet1.PacketRecords := 10;

When PacketRecords is greater than zero, then a client dataset calls GetNextPacket to fetch data as needed. Newly fetched packets are appended to the end of the data already in the client dataset.

GetNextPacket returns the number of records it fetches. If the return value is the same as PacketRecords, the end of available records was not encountered. If the return value is greater than 0 but less than PacketRecords, the last record was reached during the fetch operation. Finally, if GetNextPacket returns 0, then there are no more records to fetch.

You can also use PacketRecords to fetch metadata information about a database from the application server. To retrieve metadata information, set PacketRecords to 0.

Note:	Automatic fetching of records is controlled by the FetchOnDemand property. When FetchOnDemand is True (the default) automatic fetching is enabled. To prevent automatic fetching of records as needed, set FetchOnDemand to False. When FetchOnDemand is False, the application must explicitly call GetNextPacket to fetch records.

Making data read-only

To prevent users from modifying data, set ReadOnly property for the client dataset to True. Setting ReadOnly to True sets the CanModify property to False.

Tip:	Unlike other kinds of datasets, you do not need to close a client dataset to change its read-only status. An application can make a client dataset read-only or not on a temporary basis at any time merely by changing the current setting of the ReadOnly property.

Navigating through data

If an application uses standard data-aware controls, then a user can navigate through a client dataset's records just as for any other dataset. You can also navigate programmatically through records using standard dataset methods such as First, GotoKey, Last, Next, and Prior. For more information about these methods, see the online reference.

Client datasets can also position the cursor at any specific record in the dataset by using the RecNo property. Ordinarily an application uses RecNo to determine the record number of the current record. Applications can, however, set RecNo to a particular record number to make that record the current one.

Applications can also use bookmarks to mark and navigate to specific records. For more information about bookmarking, see "Understanding datasets."

Finally, to restrict users to a subset of available data on a temporary basis, applications can use ranges and filters. When you apply a range or a filter, the client dataset fetches all data, but displays only the data that meets the range or filter conditions. For more information about using filters, see "Understanding datasets." For more information about ranges, see "Working with tables."


Editing data

Section topics 	See also

You can use data-aware controls to let users edit records at run time, and you can programmatically edit records as well. When edits occurs, the insertions, deletions, and modifications are not stored in the Data property. Instead they are stored in an internal change log, except when the log is intentionally disabled. Edits in the change log remain there until they are removed or discarded by the application.

Counting changed records

The ChangeCount property indicates the current number of change records in the change log. If ChangeCount is 0, there are no changes recorded in the log.

Disabling logging of changes

The LogChanges property enables you to disable logging temporarily. When LogChanges is True, changes are recorded in the log. When LogChanges is False, changes are not logged. You might disable the change log for singel-tiered applications because you do not need multi-level undo capabilities in your application as described in the following section.

Undoing changes one at a time

Even though a record's original version remains unchanged in Data, each time a user edits a record, leaves it, and returns to it, the user sees the last changed version of the record. If a user or application edits a record a number of times, each changed version of the record is stored in the change log as a separate entry.

Storing each change to a record makes it possible to support multiple levels of undo operations should it be necessary to restore a record's previous state. To remove the last change to a record, call UndoLastChange. UndoLastChange takes a Boolean parameter, FollowChange, that indicates whether or not to reposition the cursor on the restored record (True), or to leave the cursor on the current record (False). If there are several changes to a single record, each call to UndoLastChange removes another layer of edits. UndoLastChange returns a Boolean value indicating success or failure to remove a change. If the removal occurs, UndoLastChange returns True.

Undoing all changes to a single record

Instead of removing each layer of changes to a single record, you can remove them all at once. To remove all changes to a record, select the record, and call RevertRecord. RevertRecord removes any changes to the current record from the change log.

Removing all changes in the change log

You can abandon all changes recorded in the change log by calling CancelUpdates. CancelUpdates clears the change log, effectively discarding all edits to all records. Be careful when you call CancelUpdates. After you call CancelUpdates, you cannot recover any changes that were in the log.


Merging changes into Data

Section topics 	See also

If you use a client dataset in a single-tiered application, then when you edit data, the changes you make are recorded in the change log, but the changes do not affect the original version of the data.

In these cases, to make your changes permanent, call MergeChangeLog to merge changed versions of records in the change log into Data. MergeChangeLog overwrites records in Data with any changed field values in the change log.

Note:	Do not call MergeChangeLog for client applications that are connected to an application server. In this case, call ApplyUpdates to write changes to the database. For more information, see "Applying updates to the database."

After MergeChangeLog executes, Data contains an amalgam of existing data, and any changes that were in the change log. This amalgam becomes the new Data baseline against which further changes can be made. MergeChangeLog clears the change log of all records and resets the ChangeCount property to 0.


Applying updates to the database

Section topics 	See also

If a client dataset is used in the client application portion of a multi-tiered database application, then changes recorded by the client dataset exist only on the client until they are applied to a database through the application server. To apply client dataset changes to the database, call the client dataset's ApplyUpdates method. ApplyUpdates takes the changes in the change log, and sends them as a data packet (called Delta) to the application server.

ApplyUpdates takes one parameter, MaxErrors. MaxErrors is an integer that indicates the error tolerance to permit before canceling the update process. If MaxErrors is 0, then as soon as an update error occurs on the application server, the entire update process is terminated. No changes are written to the database, and the client dataset's change log remains intact. If MaxErrors is -1, any number of errors is tolerated, and the change log contains all records that could not be successfully applied. If MaxErrors is a positive value, and more errors occur than are permitted by MaxErrors, all updates are aborted. If fewer errors occur than specified by MaxErrors, all records successfully applied are automatically cleared from the client dataset's change log.

ApplyUpdates calls the Reconcile function to write updates to the database. Reconcile is an error-handling routine that calls the ApplyUpdates function of a provider component on the application server. The provider component's ApplyUpdates function actually writes the updates to the database, and attempts to correct any errors it encounters. Records that it cannot apply because of error conditions are sent back to the client dataset's Reconcile method. Reconcile attempts to correct any remaining errors by calling the OnReconcileError event handler. You must code the OnReconcileError event handler to correct errors. For more information about creating and using OnReconcileError, see "Creating a multi-tiered application."

Finally, Reconcile removes successfully applied changes from the change log and updates Data to reflect the newly updated records. When Reconcile completes, ApplyUpdates reports the number of errors that occurred.


Saving data to a file

Section topics 	See also

If you use a client dataset in a single-tiered application, then when you edit data and merge it, the changes you make exist only in memory. To make a permanent record of your changes, you must write them disk using the SaveToFile method.

SaveToFile takes one parameter, a string that specifies the file into which to write data. The file name can be a fully qualified path name, if appropriate. If the file already exists, its current contents are completely overwritten.

For single-tiered applications, saving data to a file completes the updating process. For client applications that support the briefcase model, writing a file to disk is the first step in the updating process. Eventually the data on disk must be applied to a database server through a provider on the application server. Usually this is done by rerunning the client application networked to an application server, establishing a link to the application server, loading the file from disk, and calling ApplyUpdates to write the disk data to the database server. For more information about using the briefcase model and merging changes with the application server, see "Creating a multi-tiered application."


Sorting and indexing

Section topics 	See also

If a client dataset is used in a multi-tiered application, it inherits a default index and sort order based on the data it receives from the application server. The default index is called DEFAULT_ORDER. You can use this ordering, but you cannot change or delete the index.

In addition to the default index, the client dataset maintains a second index, called CHANGEINDEX, on the changed records stored in the change log (Delta property). CHANGEINDEX orders all records in Delta as they would appear if they were comprised the complete set of records in a dataset. CHANGEINDEX is based on the same ordering inherited from DEFAULT_ORDER. As with DEFAULT_ORDER, you cannot change or delete the CHANGEINDEX index.

You can also use other existing indexes for a dataset, and you can create your own indexes. The following topics describe how to create and use your indexes with client datasets.


Adding a new index

Section topics 	See also

To create a new index for a client dataset, call AddIndex. AddIndex takes three parameters: the name of the index to create, a list of existing key fields that make up the index, and a set of index options.

Tip:	You can index and sort on internally calculated fields with client datasets.

Indexes you create are sorted in ascending alphabetical order according to your machine's locale. In the index options parameter you can add ixDescending to the set of options to override this default.

Sorting of string fields in indexes is case sensitive by default. In the index options parameter you can add ixCaseInsensitive to ignore case when sorting.


Deleting an index

Section topics 	See also

To remove an index you created for a client dataset, call DeleteIndex and specify the name of the index to remove. You cannot remove the DEFAULT_ORDER and CHANGEINDEX indexes.


Switching among indexes

Section topics 	See also

To use a different index with a client dataset when more than one index is available, use the IndexName property to select the index to use. At design time you can select from available indexes in IndexName property drop-down box in the Object Inspector.


Indexing on the fly

Section topics 	See also

Instead of creating an index that becomes part of the client dataset, you can create a temporary index on the fly by specifying fields to use for indexing in the IndexFieldNames property. Separate field names with semi-colons. Ordering of field names in the list is significant.


Working with another view of a dataset

Section topics 	See also

TClientDataSet provides the CloneCursor procedure to enable you to work with a second view of a specified client dataset at run time. CloneCursor provides a second client dataset that shares the original client dataset's data.

CloneCursor takes two parameters: Source specifies the client dataset to clone.. Reset specifies whether or not to clone any ranges or filters currently in effect for the source dataset. When Reset is False, a cloned client dataset is opened, and any ranges or filters in effect for the original are activated for the clone. To create a clone of a client dataset without applying its current ranges and filters, specify True for Reset. For example, the following statement clones ClientDataSet1 and does not apply any ranges or filters to the clone:

ClientDataSet2.CloneCursor(ClientDataSet1, True);


Creating a new dataset

Section topics 	See also

The CreateDataSet procedure of TClientDataSet enables you to create a client dataset on the fly at run time.

To create a dataset:

1. 	Specify new field definitions in the client dataset's FieldDefs property.

2. 	Set new index definitions in the client dataset's IndexDefs property.

3. 	Call CreateDataSet. This procedure builds a new dataset.


See also

TClientDataSet

TDataSet

TProvider

Understanding datasets

Working with stored procedures

Working with queries

Working with tables

Data Access components


Working with data sources

Section topics 	See also

These topics describe the TDataSource component that acts as a conduit between a dataset and the data-aware, visual control components in a form.

Every dataset must have a corresponding data source component. A data source component links visual database controls on forms to a dataset. Visual database controls receive data from and send data to data source components. A data source component channels data from a dataset to visual controls, and from visual controls back to the dataset.


Establishing a database-to-data control link

Section topics 	See also

A TDataSource component is a nonvisual database component that acts as a conduit between a dataset and data-aware components on a form that enable the display, navigation, and editing of the data underlying the dataset. All datasets must be associated with a data source component in order for their data to be displayed and manipulated in data-aware controls on a form. Similarly, each data-aware control needs to be associated with a data source component in order to have data to display and manipulate. You also use data source components to link datasets in master-detail relationships.

You place a data source component in a data module or form just as you place other nonvisual database components. You should place at least one data source component for each dataset component in a data module or form.


Using TDataSource properties

Section topics 	See also

TDataSource has only a few published properties. The following sections discuss these key properties and how to set them at design time and run time.

Setting the DataSet property

The DataSet property specifies the name of the dataset from which the a data source component gets its data. At design time you can select a dataset from the drop-down list in the Object Inspector. At run time you can switch the dataset for a data source component as needed. For example, the following code swaps the dataset for the CustSource data source component between Customers and Orders:

with CustSource do

begin

 if DataSet = 'Customers' then

   DataSet := 'Orders'

 else

   DataSet := 'Customers';

end;

You can also set the DataSet property to a dataset on another form to synchronize the data controls on the two forms. For example:

procedure TForm2.FormCreate (Sender : TObject);

begin

 DataSource1.Dataset := Form1.Table1;

end;

Setting the Name property

Name enables you to specify a meaningful name for a data source component that distinguishes it from all other data sources in your application. The name you supply for a data source component is displayed below the component's icon in a data module.

Generally, you should provide a name for a data source component that indicates the dataset with which it is associated. For example, suppose you have a dataset called Customers, and that you link a data source component to it by setting the data source component's DataSet property to "Customers." To make the connection between the dataset and data source obvious in a data module, you should set the Name property for the data source component to something like "CustomersSource".

Setting the Enabled property

The Enabled property determines if a data source component is connected to its dataset. When Enabled is True, the data source is connected to a dataset.

You can temporarily disconnect a single data source from its dataset by setting Enabled to False. When Enabled is False, all data controls attached to the data source component go blank and become inactive until Enabled is set to True. It is recommended, however, to control access to a dataset through a dataset component's DisableControls and EnableControls methods because they affect all attached data sources.

Setting the AutoEdit property

The AutoEdit property of TDataSource specifies whether datasets connected to the data source automatically enter Edit state when the user starts typing in data-aware controls linked to the dataset. If AutoEdit is True (the default), Delphi automatically puts the dataset in Edit state when a user types in a linked data-aware control. Otherwise, a dataset enters Edit state only when the application explicitly calls its Edit method. For more information about dataset states, see "Determining and setting dataset states."


Using TDataSource events

Section topics 	See also

TDataSource has three event handlers associated with it:

OnDataChange

OnStateChange

OnUpdateData

Using the OnDataChange event

OnDataChange is called whenever the cursor moves to a new record. When an application calls Next, Previous, Insert, or any method that leads to a change in the cursor position, then an OnDataChange is triggered.

This event is useful if an application is keeping components synchronized manually.

Using the OnUpdateData event

OnUpdateData is called whenever the data in the current record is about to be updated. For instance, an OnUpdateData event occurs after Post is called, but before the data is actually posted to the database.

This event is useful if an application uses a standard (non-data aware) control and needs to keep it synchronized with a dataset.

Using the OnStateChange event

OnStateChange is called whenever the state for a data source's dataset changes. A dataset's State property records its current state. OnStateChange is useful for performing actions as a TDataSource's state changes.

For example, during the course of a normal database session, a dataset's state changes frequently. To track these changes, you could write an OnStateChange event handler that displays the current dataset state in a label on a form. The following code illustrates one way you might code such a routine. At run time, this code displays the current setting of the dataset's State property and updates it every time it changes:

procedure TForm1.DataSource1.StateChange(Sender:TObject);

var

 S:String;

begin

 case CustTable.State of

   dsInactive: S := 'Inactive';

   dsBrowse: S := 'Browse';

   dsEdit: S := 'Edit';

   dsInsert: S := 'Insert';

   dsSetKey: S := 'SetKey';

 end;

 CustTableStateLabel.Caption := S;

end;

Similarly, OnStateChange can be used to enable or disable buttons or menu items based on the current state:

procedure Form1.DataSource1.StateChange(Sender: TObject);

begin

 CustTableEditBtn.Enabled := (CustTable.State = dsBrowse);

 CustTableCancelBtn.Enabled := CustTable.State in [dsInsert, dsEdit, dsSetKey];

 ...

end;


See also

TDataSource

Understanding datasets

Determining and setting dataset states

Data Access components


Working with field components

Section topics 	See also

These topics describe the properties, events, and methods common to the TField object and its descendants. Descendants of TField represent individual database columns in datasets. These topics also describe how to use descendant field components to control the display and editing of data in your applications.

You never directly use a TField component in your applications. By default, when you first place a dataset in your application and open it, Delphi automatically assigns a dynamic, data-type-specific descendant of TField to represent each column in the database table(s). At design time you can override dynamic field defaults by invoking the Fields editor to create persistent fields that replace Delphi's defaults.

The following table lists each type of descendant field component , its standard purpose, and, where appropriate, the range of values it can represent:

Component name	Purpose
TAutoIncField	Whole number with a range of -2,147,483,648 to 2,147,483,647. Used in Paradox for fields whose values are automatically incremented.
TBCDField	Real number with a fixed number of decimal places, accurate to 18 digits. Range depends on the number of decimal places.
TBooleanField	True or False values.
TblobField	Binary data: Theoretical maximum limit: 2GB
TBytesField	Binary data: Theoretical maximum limit: 2GB
TCurrencyField	Real numbers with a range of 5.0 * 10-324 to 1.7 * 10308. Used in Paradox for fields with two decimals of precision.
TDateField	Date value.
TDateTimeField	Date and time value.
TFloatField	Real numbers with a range of 5.0 * 10-324 to 1.7 * 10308
TBytesField	Binary data: Maximum number of bytes: 255
TIntegerField	Whole number with a range of -2,147,483,648 to 2,147,483,647.
TMemoField	Text data: Theoretical maximum limit: 2GB
TNumericField	Real numbers with a range of 3.4 * 10-4932 to 1.1 * 104932
TSmallintField	Whole number with a range of -32,768 to 32,768
TStringField	String data: Maximum size in bytes: 8192, including a null termination character.
TTimeField	Time value.
TVarBytesField	Binary data: Maximum number of bytes: 255
TWordField	Whole numbers with a range of 0 to 65,535.

These topics discuss the properties and methods all field components inherit from TField. In many cases, TField declares or implements standard functionality that the descendant objects override. When several descendant objects share overridden functionality, that functionality is also described in these topics and noted for your convenience.


Understanding field components

Section topics 	See also

Like all Delphi data access components, field components are nonvisual. Field components are also not directly visible at design time. Instead they are associated with a dataset component, and provide data-aware components such as TDBEdit and TDBGrid access to database columns through that dataset.

Generally speaking, a single field component represents the characteristics of a single column in a database field, such as its data type and size. It also represents Delphi display characteristics, such as alignment, display format, and edit format. Finally, as you scroll from record to record within a dataset, a field component also enables you to view and change the value for that field in the current record. For example, a TFloatField component has four properties that directly affect the appearance of its data:

Property	Purpose
Alignment	Specifies whether data is displayed left-aligned, centered, or right-aligned.
DisplayWidth	Specifies the number of digits to display in a control at one time.
DisplayFormat	Specifies data formatting for display (e.g., how many decimal places to show.
EditFormat	Specifies how to display a value during editing.

Field components have many properties in common with one another (such as DisplayWidth and Alignment), and they have properties specific to their data types (such as Precision for TFloatField). Each of these properties affect how data appears to an application's users on a form. Some properties, such as Precision, can also affect how data entered in a control by a user is written back to the table underlying a dataset.

All field components for a dataset are either dynamic (automatically generated for you by Delphi based on the underlying structure of database tables), or persistent (generated by Delphi based on specific field names and properties you set in the Fields editor). Dynamic and persistent fields have different strengths and are appropriate for different types of applications. The following sections describe dynamic and persistent fields in more detail and offer advice on choosing between them.


Dynamic field components

Section topics 	See also

Dynamically generated field components are the Delphi default. In fact, all field components for any dataset start out as dynamic fields the first time you place a dataset on a data module, associate the dataset with a database, and open it. A field component is dynamic if it is created automatically by Delphi based on the underlying physical characteristics of the columns in one or more database tables accessed by a dataset. Delphi generates one field component for each column in the underlying tables or query. The exact TField descendant created for each column in an underlying database table is determined by field type information received from the Borland Database Engine (BDE). A field component's type determines its properties and how data associated with that field is displayed in data-aware controls on a form. Dynamic fields are temporary. They exist only as long as a dataset is open.

Each time you reopen a dataset that uses dynamic fields, Delphi rebuilds a completely new set of dynamic field components for it based on the current structure of the database tables underlying the dataset. If the columns in those database tables are changed, then the next time you open a dataset that uses dynamic field components, the field components that Delphi generates are also changed to match.

Use dynamic fields in applications that must be flexible about data display and editing. For example, to create a database exploration tool like the SQL Explorer, you must use dynamic fields because every database table has different numbers and types of columns. You might also want to use dynamic fields in applications where user interaction with data mostly takes place inside grid components and you know that the database tables used by the application change frequently.

To use dynamic fields in an application:

1.	Place datasets and data sources in a data module.

2.	Associate the datasets with database tables and queries, and associate the data sources with the datasets.

3.	Place data-aware controls in the application's forms, add the data module to each uses clause for each form's unit, and associate each data-aware control with a data source in the module.

4.	Open the datasets.

Aside from ease of use, dynamic fields can be limiting. Without writing code, you cannot change the display and editing defaults for dynamic fields, you cannot safely change the order in which dynamic fields are displayed, and you cannot prevent access to any fields in the dataset. You cannot create additional fields for the dataset, such as calculated fields or lookup fields, and you cannot override a dynamic field's default data type. To gain control and flexibility over fields in your database applications, you need to invoke the Fields editor to create persistent field components for your datasets.


Persistent field components

Section topics 	See also

By default, dataset fields are dynamic. Their properties and availability are automatically set by Delphi and cannot be changed in any way. To gain control over a field's properties and events so that you can set or change the field's visibility or display characteristics at design time or run time, create new fields based on existing fields in a dataset, or validate data entry, you must create persistent fields for the dataset.

At design time you can--and should--use the Fields editor to create persistent lists of the field components used by the datasets in your application. Persistent field component lists are stored in your application, and do not change even if the structure of a database underlying a dataset is changed. All fields in a dataset are either persistent or dynamic.

Creating persistent field components offers these advantages. You can:

Restrict the fields in your dataset to a subset of the columns available in the underlying database.

Add field components to the list of persistent components.

Remove field components from the list of persistent components to prevent your application from accessing particular columns in an underlying database.

Define new fields--usually to replace existing fields--based on columns in the table or query underlying a dataset.

Define calculated fields that compute their values based on other fields in the dataset.

Define lookup fields that compute their values based on fields in other datasets.

Modify field component display and edit properties.

A persistent field is one that Delphi generates based on field names and properties you specify in the Fields editor. Once you create persistent fields with the Fields editor, you can also create event handlers for them that respond to changes in data values and that validate data entries.

Note:	When you create persistent fields for a dataset, only those fields you select are available to your application at design time and run time. At design time you can always choose Add Fields from the Fields editor to add or remove persistent fields for a dataset.

All fields used by a single dataset are either persistent or dynamic. You cannot mix field types in a single dataset. If you create persistent fields for a dataset, and then want to revert to dynamic fields, you must remove all persistent fields from the dataset.For more information about dynamic fields, see "Dynamic field components."

Note:	One of the primary uses of persistent fields is to gain control over the appearance and display of data. You can also control data appearance in other ways. For example, you can use the Data Dictionary to assign field attributes to a field component. You can also control the appearance of columns in data-aware grids. For more information about the Data Dictionary, see "Creating attribute sets for field components." To learn about controlling column appearance in grids, see "Working with grids."


Creating persistent fields

Section topics 	See also

Persistent field components created with the Fields editor provide efficient, readable, and type-safe programmatic access to underlying data. Using persistent field components guarantees that each time your application runs, it always uses and displays the same columns, in the same order even if the physical structure of the underlying database has changed. Data-aware components and program code that rely on specific fields always work as expected. If a column on which a persistent field component is based is deleted or changed, then Delphi generates an exception rather than running the application against a nonexistent column or mismatched data.

To create persistent fields for a dataset:

1.	Place a dataset in a data module.

2.	Set the DatabaseName property for the dataset.

3.	Set the TableName property (for a TTable), or the SQL property (for a TQuery).

4.	Double-click the dataset component in the data module to invoke the Fields editor. The Fields editor contains a title bar, navigator buttons, and a list box.

The title bar of the Fields editor displays both the name of the data module or form containing the dataset, and the name of the dataset itself. For example, if you open the Customers dataset in the CustomerData data module, the title bar displays 'CustomerData.Customers', or as much of the name as fits.

Below the title bar is a set of navigation buttons that enable you to scroll one-by-one through the records in an active dataset at design time, and to jump to the first or last record. The navigation buttons are dimmed if the dataset is not active or if the dataset is empty.

The list box displays the names of persistent field components for the dataset. The first time you invoke the Fields editor for a new dataset, the list is empty because the field components for the dataset are dynamic, not persistent. If you invoke the Fields editor for a dataset that already has persistent field components, you see the field component names in the list box.

5.	Choose Add Fields from the Fields editor context menu.

6.	Select the fields to make persistent in the Add Fields dialog box. By default all fields are selected when the dialog box opens. Any fields you select become persistent fields.

The Add Fields dialog box closes, and the fields you selected appear in the Fields editor list box. Fields in the Fields editor list box are persistent. If the dataset is active, note, too, that the Next and Last navigation buttons above the list box are enabled.

From now on, each time you open the dataset, Delphi no longer creates dynamic field components for every column in the underlying database. Instead it only creates persistent components for the fields you specified.

Each time you open the dataset, Delphi verifies that each non-calculated persistent field exists or can be created from data in the database. If it cannot, Delphi raises an exception warning you that the field is not valid, and does not open the dataset.


Arranging the order of persistent fields

Section topics 	See also

The order in which persistent field components are listed in the Fields editor list box is the default order in which the fields appear in a data-aware grid component. You can change field order by dragging and dropping fields in the list box.

To change the order of fields:

1.	Select the fields. You can select and order one or more fields at a time.

2.	Drag the fields to a new location.

If you select a non-contiguous set of fields and drag them to a new location, they are inserted as a contiguous block. Within the block the order of fields to one another does not change.

Alternatively, you can select the field, and use Ctrl-Up and Ctrl-Dn to change an individual field's order in the list.


Defining new persistent fields

Section topics 	See also

Besides making existing dataset fields into persistent fields, you can also create special persistent fields as additions to or replacements of the other persistent fields in a dataset. The following table lists the types of additional fields you can create:

Field kind	Purpose
Data	Replaces an existing field (for example to change its data type, based on columns in the table or query underlying a dataset.
Calculated	Displays values calculated at run time by a dataset's OnCalcFields event handler.
InternalCalc	Displays values calculated at run time by a client dataset and stored with its data.
Lookup	Retrieve values from a specified dataset at run time based on search criteria you specify.

These types of persistent fields are only for display purposes. The data they contain at run time are not retained either because they already exist elsewhere in your database, or because they are temporary. The physical structure of the table and data underlying the dataset is not changed in any way.

To create a new persistent field component, invoke the context menu for the Fields editor and choose New field. The New Field dialog box appears.

The New Field dialog box contains three group boxes: Field properties, Field type, and Lookup definition.

The Field type radio group enables you to specify the type of new field component to create. The default type is Data. If you choose Lookup, the Dataset and Source Fields edit boxes in the Lookup definition group box are enabled. You can also create Calculated fields, and if you're working with a TClientDataSet component, you can also create InternalCalc fields.

The Field properties group box enables you to enter general field component information. Enter the component's field name in the Name edit box. The name you enter here corresponds to the field component's FieldName property. Delphi uses this name to build a component name in the Component edit box. The name that appears in the Component edit box corresponds to the field component's Name property and is only provided for informational purposes (Name contains the identifier by which you refer to the field component in your source code). Delphi discards anything you enter directly in the Component edit box.

The Type combo box in the Field properties group enables you to specify the field component's data type. You must supply a data type for any new field component you create. For example, to display floating point currency values in a field, select Currency from the drop-down list. The Size edit box enables you to specify the maximum number of characters that can be displayed or entered in a string-based field or the size of Bytes and VarBytes fields. For all other data types, Size is meaningless.

The Lookup definition group box is only used to create lookup fields. For more information, see "Defining a lookup field."


Defining a data field

Section topics 	See also

A data field replaces an existing field in a dataset. For example, for programmatic reasons you might want to replace a TSmallIntField with a TIntegerField. Because you cannot change a field's data type directly, you must define a new field to replace it.

Important:	Even though you define a new field to replace an existing field, the field you define must derive its data values from an existing column in a table underlying a dataset.

To create a replacement data field for a field in a table underlying a dataset, follow these steps

1.	Remove the field from the list of persistent fields assigned for the dataset, and then choose Define New Field from the context menu.

1.	In the New Field dialog box, enter the name of an existing field in the database table in the Name edit box. Do not enter a new field name. You are actually specifying the name of the field from which your new field will derive its data.

1.	Choose a new data type for the field from the Type combo box. The data type you choose should be different from the data type of the field you are replacing. You cannot replace a string field of one size with a string field of another size. Note that while the data type should be different, it must be compatible with the actual data type of the field in the underlying table.

1.	Enter the size of the field in the Size edit box, if appropriate. Size is only relevant for fields of type TStringField, TBytesField, and TVarBytesField.

1.	Select Data in the Field type radio group if it is not already selected.

1.	Choose OK. The New Field dialog box closes, the newly defined data field replaces the existing field you specified in Step 1, and the component declaration in the data module or form’s type declaration is updated.

To edit the properties or events associated with the field component, select the component name in the Field editor list box, then edit its properties or events with the Object Inspector. For more information about editing field component properties and events, see "Setting persistent field properties and events."


Defining a calculated field

Section topics 	See also

A calculated field displays values calculated at run time by a dataset's OnCalcFields event handler. For example, you might create a string field that displays concatenated values from other fields.

To create a calculated field in the New Field dialog box:

1.	Enter a name for the calculated field in the Name edit box. Do not enter the name of an existing field.

2.	Choose a data type for the field from the Type combo box.

3.	Enter the size of the field in the Size edit box, if appropriate. Size is relevant only for fields of type TStringField, TBytesField, and TVarBytesField.

4.	Select Calculated in the Field type radio group.

5.	Choose OK. The newly defined calculated field is automatically added to end of the list of persistent fields in the Field editor list box, and the component declaration is automatically added to the form's type declaration in the source code.

6.	Place code that calculates values for the field in the OnCalcFields event handler for the dataset. For more information about writing code to calculate field values, see "Programming a calculated field."

Note:	To edit the properties or events associated with the field component, select the component name in the Field editor list box, then edit its properties or events with the Object Inspector. For more information about editing field component properties and events, see "Setting persistent field properties and events."

If you are working with a client dataset or query component, you can also create an InternalCalc field. You create and program an internally calculated field just like you do a calculated field. For a client dataset, the significant difference between these types of calculated fields is that the values calculated for an InternalCalc field are stored and retrieved as part of the client dataset's data.To create an InternalCalc field, select the InternalCalc radio button in the Field type group.


Programming a calculated field

Section topics 	See also

After you define a calculated field, you must write code to calculate its value. Otherwise it always has a null value. Code for a calculated field is placed in the OnCalcFields event for its dataset.

To program a value for a calculated field:

1.	Select the dataset component from the Object Inspector drop-down list.

2.	Choose the Object Inspector Events page.

3.	Double-click the OnCalcFields property to bring up or create a CalcFields procedure for the dataset component.

4.	Write the code that sets the values and other properties of the calculated field as desired.

For example, suppose you have created a CityStateZip calculated field for the Customers table on the CustomerData data module. CityStateZip should display a company's city, state, and zip code on a single line in a data-aware control.

To add code to the CalcFields procedure for the Customers table, select the Customers table from the Object Inspector drop-down list, switch to the Events page, and double-click the OnCalcFields property.

The TCustomerData.CustomersCalcFields procedure appears in the unit's source code window. Add the following code to the procedure to calculate the field:

CustomersCityStateZip.Value := CustomersCity.Value + ', ' + CustomersState.Value
  + ' ' + CustomersZip.Value;


Defining a lookup field

Section topics 	See also

A lookup field is a read-only field that displays values at run time based on search criteria you specify. In its simplest form, a lookup field is passed the name of an existing field to search on, a field value to search for, and a different field in a lookup dataset whose value it should display.

For example, consider a mail-order application that enables an operator to use a lookup field to determine automatically the city and state that correspond to a zip code a customer provides. In that case, the column to search on might be called ZipTable.Zip, the value to search for is the customer's zip code as entered in Order.CustZip, and the values to return would be those in the ZipTable.City and ZipTable.State columns for the record where ZipTable.Zip matches the current value in the Order.CustZip field.

To create a lookup field in the New Field dialog box:

1.	Enter a name for the lookup field in the Name edit box. Do not enter the name of an existing field.

2.	Choose a data type for the field from the Type combo box.

3.	Enter the size of the field in the Size edit box, if appropriate. Size is only relevant for fields of type TStringField, TBytesField, and TVarBytesField.

4.	Select Lookup in the Field type radio group. Selecting Lookup enables the Dataset and Key Fields combo boxes.

5.	Choose from the Dataset combo box drop-down list the dataset in which to look up field values. The lookup dataset must be different from the dataset for the field component itself, or a circular reference exception is raised at run time. Specifying a lookup dataset enables the Lookup Keys and Result Field combo boxes.

6.	Choose from the Key Fields drop-down list a field in the current dataset for which to match values. To match more than one field, enter field names directly instead of choosing from the drop-down list. Separate multiple field names with semicolons.

7.	Choose from the Lookup Keys drop-down list a field in the lookup dataset to match against the Source Fields field you specified in step 6. To specify more than one field, enter field names directly instead. Separate multiple field names with semicolons.

8.	Choose from the Result Field drop-down list a field in the lookup dataset to return as the value of the lookup field you are creating. To return values from more than one field in the lookup dataset, enter field names directly instead. Separate multiple field names with semicolons.

When you design and run your application, lookup field values are determined before calculated field values are calculated. You can base calculated fields on lookup fields, but you cannot base lookup fields on calculated fields. You can use the LookupCache property to hone this behavior. LookupCache determines whether the values of a lookup field are cached in memory when a dataset is first opened, or looked up dynamically every time the current record in the dataset changes.

Set LookupCache to True to cache the values of a lookup field when the LookupDataSet is unlikely to change and the number of distinct lookup values is small. Caching lookup values can speed performance, because the lookup values for every set of LookupKeyFields values are preloaded when the DataSet is opened. When the current record in the DataSet changes, the field object can locate its Value in the cache, rather than accessing the LookupDataSet. This performance improvement is especially dramatic if the LookupDataSet is on a network where access is slow.

Tip:	Caching lookup values can be especially useful when working with client datasets in single-tier applications. Such an application can set LookupCache to True and supply lookup values programmatically rather than from a seondary dataset.

If every record of DataSet has different values for KeyFields, the overhead of locating values in the cache can be greater than any performance benefit provided by the cache. The overhead of locating values in the cache increases with the number of distinct values that can be taken by KeyFields.

If LookupDataSet is volatile, caching lookup values can lead to inaccurate results. Call RefreshLookupList to update the values in the lookup cache. RefreshLookupList regenerates the LookupList property, which contains the value of the LookupResultField for every set of LookupKeyFields values.

When setting LookupCache at runtime, call RefreshLookupList to initialize the cache.


Deleting persistent field components

Section topics 	See also

Deleting a persistent field component is useful for accessing a subset of available columns in a table, and for defining your own persistent fields to replace a column in a table.

To remove one or more persistent field components for a dataset:

1.	Select the field(s) to remove in the Fields editor list box.

2.	Press Del.

Note:	You can also delete selected fields by invoking the context menu and choosing Delete.

Fields you remove are no longer available to the dataset and cannot be displayed by data-aware controls. You can always re-create persistent field components that you delete by accident, but any changes previously made to its properties or events is lost. For more information, see "Creating persistent fields."

Note:	If you remove all persistent field components for a dataset, then Delphi again generates dynamic field components for every column in the database table underlying the dataset.


Setting persistent field properties and events

Section topics 	See also

You can set properties and customize events for persistent field components at design time. Properties control the way a field is displayed by a data-aware component, for example, whether it can appear in a TDBGrid, or whether its value can be modified. Events control what happens when data in a field is fetched, changed, set, or validated.

To set the properties of a field component or write customized event handlers for it, select the component in the Fields editor, or select it from the component list in the Object Inspector.


Setting display and edit properties at design time

Section topics 	See also

To edit the display properties of a selected field component, switch to the Properties page on the Object Inspector window. The following table summarizes display properties that can be edited.

Property	Purpose
Alignment	Left justifies, right justifies, or centers a field contents within a data-aware component.
ConstraintErrorMessage	Specifies the text to display when edits clash with a constraint condition.
CustomConstraint	Specifies a local constraint to apply to data during editing.
Currency	Numeric fields only. True: displays monetary values.False (default): does not display monetary values.
DisplayFormat	Specifies the format of data displayed in a data-aware component.
DisplayLabel	Specifies the column name for a field in a data-aware grid component.
DisplayWidth	Specifies the width, in characters, of a grid column that display this field.
EditFormat	Specifies the edit format of data in a data-aware component.
EditMask	Limits data-entry in an editable field to specified types and ranges of characters, and specifies any special, non-editable characters that appear within the field (hyphens, parentheses, and so on).
FieldKind	Specifies the type of field to create.
FieldName	Specifies the actual name of a column in the table from which the field derives its value and data type.
HasConstraints	Indicates whether or not there are constraint conditions imposed on a field.
ImportedConstraint	Specifies an SQL constraint imported from the Data Dictionary or an SQL server.
Index	Specifies the order of the field in a dataset.
LookupDataSet	Specifies the table used to look up field values when Lookup is True.
LookupKeyFields	Specifies the field(s) in the lookup dataset to match when doing a lookup.
LookupResultField	Specifies the field in the lookup dataset from which to copy values into this field.
MaxValue	Numeric fields only. Specifies the maximum value a user can enter for the field.
MinValue	Numeric fields only. Specifies the minimum value a user can enter for the field.
Name	Specifies the component name of the field component within Delphi.
Origin	Specifies the name of the field as it appears in the underlying database.
Precision	Numeric fields only. Specifies the number of significant digits.
ReadOnly	True: Displays field values in data-aware components, but prevents editing.False (the default): Permits display and editing of field values.
Size	Specifies the maximum number of characters that can be displayed or entered in a string-based field, or the size, in bytes, of TBytesField and TVarBytesField fields.
Tag	Long integer bucket available for programmer use in every component as needed.
Transliterate	True (default): specifies that translation to and from the respective locales will occur as data is transferred between a dataset and a database.False: Locale translation does not occur.
Visible	True (the default): Permits display of field in a data-aware grid component.False: Prevents display of field in a data-aware grid component.User-defined components can make display decisions based on this property.

Not all properties are available for all field components. For example, a field component of type TStringField does not have Currency, MaxValue, or DisplayFormat properties, and a component of type TFloatField does not have a Size property.

While the purpose of most properties is straight-forward, some properties, such as Calculated, require additional programming steps to be useful. Others, such as DisplayFormat, EditFormat, and EditMask, are interrelated; their settings must be coordinated. For more information about using DisplayFormat, EditFormat, and EditMask, see "Controlling and masking user input."


Setting field component properties at run time

You can use and manipulate the properties of field component at run time. For example, the following code sets the ReadOnly property for the CityStateZip field in the Customers table to True:

CustomersCityStateZip.ReadOnly := True;

And this statement changes field ordering by setting the Index property of the CityStateZip field in the Customers table to 3:

CustomersCityStateZip.Index := 3;


Creating attribute sets for field components

Section topics 	See also

When several fields in the datasets used by your application share common formatting properties (such as Alignment, DisplayWidth, DisplayFormat, EditFormat, MaxValue, MinValue, and so on), it is more convenient to set the properties for a single field, then store those properties as an attribute set in the Data Dictionary. Attribute sets stored in the data dictionary can be easily applied to other fields.

To create an attribute set based on a field component in a dataset:

1.	Double-click the dataset to invoke the Fields editor.

2.	Select the field for which to set properties.

3.	Set the desired properties for the field in the Object Inspector.

4.	Right-click the Fields editor list box to invoke the context menu.

5.	Choose Save Attributes to save the current field's property settings as an attribute set in the Data Dictionary.

The name for the attribute set defaults to the name of the current field. You can specify a different name for the attribute set by choosing Save Attributes As instead of Save Attributes from the context menu.

Note:	You can also create attribute sets directly from the SQL Explorer. When you create an attribute set from the data dictionary, the set is not applied to any fields, but you can specify two additional attributes in the set: a field type (such as TFloatField, TStringField, and so on) and a data-aware control (such as TDBEdit, TDBCheckBox, and so on) that is automatically placed on a form when a field based on the attribute set is dragged onto the form. For more information, see the online help for the SQL Explorer.


Associating attribute sets with field components

Section topics 	See also

When several fields in the datasets used by your application share common formatting properties (such as Alignment, DisplayWidth, DisplayFormat, EditFormat, MaxValue, MinValue, and so on), and you have saved those property settings as attribute sets in the Data Dictionary, you can easily apply the attribute sets to fields without having to recreate the settings manually for each field. In addition, if you later change the attribute settings in the Data Dictionary, those changes are automatically applied to every field associated with the set the next time field components are added to the dataset.

To apply an attribute set to a field component:

1.	Double-click the dataset to invoke the Fields editor.

2.	Select the field for which to apply an attribute set.

3.	Invoke the context menu and choose Associate attributes.

4.	Select or enter the attribute set to apply from the Attribute set name dialog box. If there is an attribute set in the Data Dictionary that has the same name as the current field, that set name appears in the edit box.

Important:	If the attribute set in the Data Dictionary is changed at a later date, you must reapply the attribute set to each field component that uses it. You can invoke the Fields editor to multi-select field components within a dataset to which to reapply attributes.


Removing attribute associations

Section topics 	See also

If you change your mind about associating an attribute set with a field, you can remove the association by following these steps:

Invoke the Fields editor for the dataset containing the field.

Select the field or fields from which to remove the attribute association.

Invoke the context menu for the Fields editor and choose Unassociate Attributes.

Important:	Unassociating an attribute set does not change any field properties. A field retains the settings it had when the attribute set was applied to it. To change these properties, select the field in the Fields editor and set its properties in the Object Inspector.


Controlling and masking user input

Section topics 	See also

The EditMask property provides a way to control the type and range of values a user can enter into a data-aware component associated with TStringField, TDateField, TTimeField, and TDateTimeField components. You can use existing masks, or create your own. The easiest way to use and create edit masks is with the Input Mask editor. You can, however, enter masks directly into the EditMask field in the Object Inspector.

Note:	For TStringField components, the EditMask property is also its display format.

To invoke the Input Mask editor for a field component:

1.	Select the component in the Fields editor or Object Inspector.

2.	Click the Properties page in the Object Inspector.

3.	Double-click the values column for the EditMask field in the Object Inspector, or click the ellipsis button. The Input Mask editor opens.

The Input Mask edit box enables you to create and edit a mask format. The Sample Masks grid lets you select from predefined masks. If you select a sample mask, the mask format appears in the Input Mask edit box where you can modify it or use it as is. You can test the allowable user input for a mask in the Test Input edit box.

The Masks button enables you to load a custom set of masks--if you have created one--into the Sample Masks grid for easy selection.

Note:	For TStringField components, the EditMask property is also its display format.


Using default formatting for numeric, date, and time fields

Section topics 	See also

Delphi provides built-in display and edit format routines and intelligent default formatting for TFloatField, TCurrencyField, TIntegerField, TSmallIntField, TWordField, TDateField, TDateTimeField, and TTimeField components. To use these routines, you need do nothing.

Default formatting is performed by the following routines:

Routine	Used by . . .
FormatFloat	TFloatField, TCurrencyField
FormatDateTime	TDateField, TTimeField, TDateTimeField
FormatCurr	TCurrencyField

Only format properties appropriate to the data type of a field component are available for a given component.

Default formatting conventions for date, time, currency, and numeric values are based on the Regional Settings properties in the Windows95 Control Panel. For example, using the default settings for the United States, a TFloatField column with the Currency property set to True sets the DisplayFormat property for the value 1234.56 to $1234.56, while the EditFormat is 1234.56.

At design time or run time you can edit the DisplayFormat and EditFormat properties of a field component to override the default display settings for that field. You can also write OnGetText and OnSetText event handlers to do custom formatting for field components at run time. For more information about setting field component properties at run time, see "Setting field component properties at run time."


Handling events

Section topics 	See also

Like all Delphi components, fields components have event handlers associated with them. By writing these handlers you can control events that affect data entered in fields through data-aware controls. The following table lists the events associated with field components:

Event	Purpose
OnChange	Called when the value for a field changes.
OnGetText	Called when the value for a field component is retrieved for display or editing.
OnSetText	Called when the value for a field component is set.
OnValidate	Called to validate the value for a field component whenever the value is changed because of an edit or insert operation.

OnGetText and OnSetText events are primarily useful to programmers who want to do custom formatting that goes beyond Delphi's built-in formatting functions. OnChange is useful for performing application-specific tasks associated with data change, such as enabling or disabling menus or visual controls. OnValidate is useful when you want to control data-entry validation in your application before returning values to a database server.

To write an event handler for a field component:

1.	Select the component.

2.	Select the Events page in the Object Inspector.

3.	Double-click the Value field for the event handler to display its source code window.

4.	Create or edit the handler code.


Working with field component methods at runtime

Section topics 	See also

Field components methods available at run time enable you to convert field values from one data type to another, and enable you to set focus to the first data-aware control in a form that is associated with a field component.

Controlling the focus of data-aware components associated with a field is important when your application performs record-oriented data validation in a dataset event handler (such as BeforePost). Validation may be performed on the fields in a record whether or not its associated data-aware control has focus. Should validation fail for a particular field in the record, you want the data-aware control containing the faulty data to have focus so that the user can enter corrections.

You control focus for a field's data-aware components with a field's FocusControl method. FocusControl sets focus to the first data-aware control in a form that is associated with a field. An event handler should call a field's FocusControl method before validating the field. The following code illustrates how to call the FocusControl method for the Company field in the Customers table:

CustomersCompany.FocusControl;

The following table lists some other field component methods and their uses. For a complete list and detailed information about using each method, see the entries for TField and its descendants in the online VCL Reference.

Method	Purpose
AssignValue	Sets a field value to a specified value using an automatic conversion function based on the field's type.
Clear	Clears the field and sets its value to nil.
GetData	Retrieves unformatted data from the field.
IsValidChar	Determines if a character entered by a user in a data-aware control to set a value is allowed for this field.
SetData	Assigns unformatted data to this field.


Displaying, converting, and accessing field values

Section topics 	See also

Data-aware controls such as TDBEdit and TDBGrid automatically display the values associated with field components. If editing is enabled for the dataset and the controls, data-aware controls can also send new and changed values to the database. In general, the built-in properties and methods of data-aware controls enable them to connect to datasets, display values, and make updates without requiring extra programming on your part. Use them whenever possible in your database applications.

Standard controls can also display and edit database values associated with field components. Using standard controls, however, may require additional programming on your part.


Displaying field component values in standard controls

Section topics 	See also

An application can access the value of a database column through the Value property
of a field component. For example, the following statement assigns the value of the CustomersCompany field to the text in a TEdit control:

Edit3.Text := CustomersCompany.Value;

This method works well for string values, but may require additional programming to handle conversions for other data types. Fortunately, field components have built-in functions for handling conversions.

Note:	You can also use variants to access and set field values. Variants are a new and flexible data type. For more information about using variants to access and set field values, see "Accessing field values with the default dataset method."


Converting field values

Section topics 	See also

Conversion functions attempt to convert one data type to another. For example, the AsString function converts numeric and Boolean values to string representations. The following table lists field component conversion functions, and which functions are recommended for field components by field-component type:

Function	TStringField	TIntegerField	TSmallintField	TWordField	TFloatField	TCurrencyField	TBCDField	TDateTimeField	TDateField	TTimeField	TBooleanField	TBytesField	TVarBytesField	TBlobField	TMemoField	TGraphicField
AsVariant	3	3	3	3	3	3	3	3	3	3	3	3	3	3	3	3
AsString		3	3	3	3	3	3	3	3	3	3	3	3	3	3	3
AsInteger	3				3	3	3
AsFloat	3	3	3	3				3	3	3
AsCurrency	3	3	3	3				3	3	3
AsDateTime	3
AsBoolean	3

Note that the AsVariant method is recommended to translate among all data types. When in doubt, use AsVariant.

In some cases, conversions are not always possible. For example, AsDateTime can be used to convert a string to a date, time, or datetime format only if the string value is in a recognizable datetime format. A failed conversion attempt raises an exception.

In some other cases, conversion is possible, but the results of the conversion are not always intuitive. For example, what does it mean to convert a TDateTimeField value into a float format? AsFloat converts the date portion of the field to the number of days since 12/31/1899, and it converts the time portion of the field to a fraction of 24 hours. The following table lists permissible conversions that produce special results:

Conversion	Result
String to Boolean	Converts "True," "False," "Yes," and "No" to Boolean. Other values raise exceptions.
Float to Integer	Rounds float value to nearest integer value.
DateTime to Float	Converts date to number of days since 12/31/1899, time to a fraction of 24 hours.
Boolean to String	Converts any Boolean value to "True" or "False."

In other cases, conversions are not possible at all. In these cases, attempting a conversion also raises an exception.

You use a conversion function as you would use any method belonging to a component: append the function name to the end of the component name wherever it occurs in an assignment statement. Conversion always occurs before an actual assignment is made. For example, the following statement converts the value of CustomersCustNo to a string and assigns the string to the text of an edit control:

Edit1.Text := CustomersCustNo.AsString;

Conversely, the next statement assigns the text of an edit control to the CustomersCustNo field as an integer:

MyTableMyField.AsInteger := StrToInt(Edit1.Text);

An exception occurs if an unsupported conversion is performed at run time.


Accessing field values with the default dataset method

Section topics 	See also

The preferred method for accessing a field's value is to use variants with the default dataset method, FieldValues. For example, the following statement puts the value of an edit box into the CustNo field in the Customers table:

Customers['CustNo'] := Edit2.Text;

Because FieldValues is the default method for a dataset, you do not need to specify its method name explicitly. The following statement, however, is identical to the previous one:

Customers.FieldValues['CustNo'] := Edit2.Text;

For more information about variants, see the Object Pascal Language Guide.


Accessing field values with a dataset's Fields property

Section topics 	See also

You can access the value of a field with the Fields property of the dataset component to which the field belongs. Accessing field values with a dataset's Fields property is useful when you need to iterate over a number of columns, or if your application works with tables that are not available to you at design time.

To use the Fields property you must know the order of and data types of fields in the dataset. You use an ordinal number to specify the field to access. The first field in a dataset is numbered 0. Field values must be converted as appropriate using the field component's conversion routine. For more information about field component conversion functions, see "Converting field values."

For example, the following statement assigns the current value of the seventh column (Country) in the Customers table to an edit control:

Edit1.Text := CustTable.Fields[6].AsString;

Conversely, you can assign a value to a field by setting the Fields property of the dataset to the desired field. For example:

begin

 Customers.Edit;

 Customers.Fields[6].AsString := Edit1.Text;

 Customers.Post;

end;


Accessing field values with a dataset's FieldByName method

Section topics 	See also

You can also access the value of a field with a dataset's FieldByName method. This method is useful when you know the name of the field you want to access, but do not have access to the underlying table at design time.

To use FieldByName, you must know the dataset and name of the field you want to access. You pass the field's name as an argument to the method. To access or change the field's value, convert the result with the appropriate field component conversion function, such as AsString or AsInteger. For example, the following statement assigns the value of the CustNo field in the Customers dataset to an edit control:

Edit2.Text := Customers.FieldByName('CustNo').AsString;

Conversely, you can assign a value to a field:

begin

 Customers.Edit;

 Customers.FieldByName('CustNo').AsString := Edit2.Text;

 Customers.Post;

end;


Checking a field's current value

Section topics 	See also

If your application uses TClientDataSet or adminsters a dataset that is the source dataset for a TProvider component on an application server, and you encounter difficulties when updating records, you can use the CurValue property to examine the field value in the record causing problems. CurValue represents the current value of the field component including changes made by other users of the database.

Use CurValue to examine the value of a field when a problem occurs in posting a value to the database. If the current field value is causing a problem, such as a key violation, when posting the value to the database, an OnReconcileError occurs. In an OnReconcileError event handler, NewValue is the unposted value that caused the problem, OldValue is the value that was originally assigned to the field before any edits were made, and CurValue is the value that is currently assigned to the field. CurValue may differ from OldValue if another user changed the value of the field after OldValue was read.


Setting a default value for a field

Section topics 	See also

You can specify how a default value for a field should be calculated at runtime using the DefaultExpression property. DefaultExpression can be any valid SQL value expression that does not refer to field values. If the expression contains literals other than numeric values, they must appear in quotes. For example, a default value of noon for a time field would be

'12:00:00'

including the quotes around the literal value.


Working with constraints

Section topics 	See also

In Delphi, field components can use SQL server constraints. In addition, your applications can create and use custom constraints that are local to your application. All constraints are rules or conditions that impose a limit on the scope or range of values that a field can store. The following sections describe working with constraints at the field component level.


Creating a custom constraint

Section topics 	See also

A custom constraint is not imported from the server like other constraints. It is a constraint that you declare, implement, and enforce in your local application. As such, custom constraints can be useful for offering a pre-validation enforcement of data entry, but a custom constraint cannot be applied against data received from or set to a server application.

To create a custom constraint:

1.	Set the CustomConstraint property to specify a constraint condition,

2.	Set ConstraintErrorMessage to the message to display when a user violates the constraint at run time.

CustomConstraint is an SQL string the specifies any application-specific constraints imposed on the field's value. Set CustomConstraint to limit the values that the user can enter into a field. CustomConstraint can be any valid SQL search expression such as

x > 0 and x < 100

The name used to refer to the value of the field can be any string that is not a reserved SQL keyword, as long as it is used consistently throughout the constraint expression.

Custom constraints are imposed in addition to any constraints to the field's value that come from the server. To see the constraints imposed by the server, read the ImportedConstraint property.


Using server constraints

Section topics 	See also

Most production SQL databases use constraints to impose conditions on the possible values for a field. For example, a field may not permit NULL values, may require that its value be unique for that column, or that its values be greater than 0 and less than 150. While you could replicate such conditions in your Delphi applications, Delphi offers the ImportedConstraint property to propagate a server's constraints locally.

ImportedConstraint is a read-only property that specifies an SQL clause that limits field values in some manner. For example:

Value > 0 and Value < 100

Do not change the value of ImportedConstraint, except to edit nonstandard or server-specific SQL that has been imported as a comment because it cannott be interpreted by the database engine.

To add additional constraints on the field value, use the CustomConstraint property. Custom constraints are imposed in addition to the imported constraints. If the server constraints change, the value of ImportedConstraint also changed but constraints introduced in the CustomConstraint property persist.

Removing constraints from the ImportedConstraint property will not change the validity of field values that violate those constraints. Removing constraints results in the constraints being checked by the server instead of locally. When constraints are checked locally, the error message supplied as the ConstraintErrorMessage property is displayed when violations are found, instead of displaying an error message from the server.


See also

TField

TStringField

TIntegerField

TSmallIntField

TWordField

TFloatField

TAutoIncField

TCurrencyField

TBCDField

TBooleanField

TDateTimeField

TDateField

TTimeField

TBlobField

TBytesField

TVarBytesField

TMemoField

TGraphicField


Working with tables

Section topics 	See also

These topics describe how to use the TTable dataset component in your database applications. A table component encapsulates the full structure of and data in an underlying database table. A table component inherits many of its fundamental properties and methods from TDataSet, TBDEDataSet, and TDBDataSet. Therefore you should be familiar with the general discussion of datasets in "Understanding datasets," and the BDE-specific discussion of datasets in "Understanding BDE-enabled datasets," before reading about the unique properties and methods of table components discussed here.


Using table components

Section topics 	See also

A table component gives you access to every row and column in an underlying database table, whether it is from Paradox, dBASE, Access, FoxPro, an ODBC-compliant database, or an SQL database on a remote server, such as InterBase, Sybase, or SQL Server.

You can view and edit data in every column and row of a table. You can work with a range of rows in a table, and you can filter records to retrieve a subset of all records in a table based on filter criteria you specify. You can search for records, copy or delete entire tables, and create master/detail relationships between tables.

Note:	A table component always references a single database table. If you need to access multiple tables with a single component, or if you are interested only in a subset of rows and columns in one or more tables, you should use a query component instead of a table component. For more information about query components, see "Working with queries."


Setting up a table component

Section topics 	See also

The following steps are general instructions for setting up a table component at design time. There may be additional steps you need to take to tailor a table's properties to the requirements of your application. To create a table component,

1.	Place a table component from the Data Access page of the Component palette in a data module or on a form, and set its Name property to a unique value appropriate to your application.

2.	Set the DatabaseName of the component to the name of the database to access.

3.	Set the TableName property to the name of the table in the database. You can select tables from the drop-down list if the DatabaseName property is already specified.

4.	Place a data source component in the data module or on the form, and set its DataSet property to the name of the table component. The data source component is used to pass a result set from the table to data-aware components for display.

To access the data encapsulated by a table component,

1.	Place a data source component from the Data Access page of the Component palette in the data module or form, and set its DataSet property to the name of the table component.

2.	Place a data-aware control, such as TDBGrid, on a form, and set the control's DataSource property to the name of the data source component placed in the previous step.

3.	Set the Active property of the table component to True.


Specifying a database location

Section topics 	See also

The DatabaseName property specifies where Delphi looks for a database table. For Paradox and dBASE, DatabaseName can be a Borland Database Engine (BDE) alias, or an explicit directory path. For SQL tables, DatabaseName must be a BDE alias.

The advantage of using BDE aliases in all cases is that you can change the data source for an entire application by simply changing the alias definition in the SQL Explorer by invoking the BDE Administration Tool from the context menu. For more information about setting and using BDE aliases, see the online help for the SQL Explorer.

To set the DatabaseName property,

1.	Set the table's Active property to False if necessary.

2.	Specify the BDE alias or directory path in the DatabaseName property.

Tip:	If your application uses database components to control database transactions, DatabaseName can be set to a local alias defined for the database component instead. For more information about database components, see "Connecting to databases."


Specifying a table name

Section topics 	See also

The TableName property specifies the table in a database to access with the table component. To specify a table:

1.	Set the table's Active property to False, if necessary.

2.	Set the DatabaseName property to a BDE alias or directory path. For more information about setting DatabaseName, see "Specifying a database location."

3.	Set the TableName property to the table to access. At design time you can choose from valid table names in the drop-down list for the TableName property in the Object Inspector. At run time, you must specify a valid name in code.

Once you specify a valid table name, you can set the table component's Active property to True to connect to the database, open the table, and display and edit data.

At run time, you can set or change the table associated with a table component by:

Setting Active to False.

Assigning a valid table name to the TableName property.

For example, the following code changes the table name for the OrderOrCustTable table component based on its current table name:

with OrderOrCustTable do

begin

 Active := False; {Close the table}

 if TableName = 'CUSTOMER.DB' then

   TableName := 'ORDERS.DB'

 else

   TableName := 'CUSTOMER.DB';

 Active := True; {Reopen with a new table}

end;


Specifying the table type for Paradox, dBASE, and ASCII tables

Section topics 	See also

If an application accesses Paradox, dBASE, or comma-delimited ASCII text tables, then the BDE uses the TableType property to determine the table's type (its expected structure). TableType is not used when an application accesses SQL-based tables on database servers.

By default TableType is set to ttDefault. When TableType is ttDefault, the BDE determines a table's type from its file-name extension. The following table summarizes the file name extensions recognized by the BDE and the assumptions it makes about a table's type:

Extension	Table type
No file extension	Paradox
.DB	Paradox
.DBF	dBASE
.TXT	ASCII text

If your local Paradox, dBASE, and ASCII text tables use the file extensions as described in the previous table, then you can leave TableType set to ttDefault. Otherwise, your application must set TableType to indicate the correct table type. The following table indicates the values you can assign to TableType:

Value	Table type
ttDefault	Table type determined automatically by the BDE
ttParadox	Paradox
ttDBase	dBASE
ttASCII	Comma-delimited ASCII text


Opening and closing a table

Section topics 	See also

To view and edit a table's data in a data-aware control such as TDBGrid, open the table. There are two ways to open a table. You can set its Active property to True, or you can call its Open method. Opening a table puts it into dsBrowse state and displays data in any active controls associated with the table's data source.

To end display and editing of data, or to change the values for a table component's fundamental properties (e.g., DatabaseName, TableName, and TableType), first post or discard any pending changes. If cached updates are enabled, call the ApplyUpdates method to write the posted changes to the database. Finally, close the table.

There are two ways to close a table. You can set its Active property to False, or you can call its Close method. Closing a table puts the table into dsInactive state. Active controls associated with the table's data source are cleared.


Controlling read/write access to a table

Section topics 	See also

By default when a table is opened, it requests read and write access for the underlying database table. Depending on the characteristics of the underlying database table, the requested write privilege may not be granted (for example, when you request write access to an SQL table on a remote server and the server restricts the table's access to read only).

There are three properties for table components that can affect an application's read and write access to a table: CanModify, ReadOnly, and Exclusive.

CanModify is a read-only property that specifies whether or not a table component is permitted read/write access to the underlying database table. After you open a table at run time, your application can examine CanModify to test whether or not the table has write access. If CanModify is False, the application cannot write to the database. If CanModify is True, your application can write to the database provided that the table's ReadOnly property is False.

ReadOnly determines whether or not a user can both view and edit data. When ReadOnly is False (the default), a user can both view and edit data. To restrict a user to viewing data, set ReadOnly to True before opening a table.

Exclusive controls whether or not an application gains sole read/write access to a Paradox, dBASE, or FoxPro table. To gain sole read/write access for these table types, set the table component's Exclusive property to True before opening the table. If you succeed in opening a table for exclusive access, other applications cannot read data from or write data to the table. Your request for exclusive access is not honored if the table is already in use when you attempt to open it.

The following statements open a table for exclusive access:

CustomersTable.Exclusive := True; {Set request for exclusive lock}

CustomersTable.Active := True; {Now open the table}

Note:	You can attempt to set Exclusive on SQL tables, but some servers may not support exclusive table-level locking. Others may grant an exclusive lock, but permit other applications to read data from the table. For more information about exclusive locking of database tables on your server, see your server documentation.


Searching for records

Section topics 	See also

Delphi provides a few ways for you to search for specific records in a table. The most flexible and preferred way to search for a record is to use the generic search methods Locate and Lookup. These methods enable you to search on any type of fields in any table, whether or not they are indexed or keyed.

Locate finds the first row matching a specified set of criteria and moves the cursor to that row.

Lookup returns values from the first row that matches a specified set of criteria, but does not move the cursor to that row.

You can use Locate and Lookup with any kind of dataset, not just TTable. For a complete discussion of Locate and Lookup, see "Understanding datasets."

Table components also continue to support Goto and Find methods for compatibility with previous versions of Delphi. While these methods are documented here to allow you to work with legacy applications, you should always use Lookup and Locate in your new applications. Moreover, you may see performance gains in existing applications if you convert them to use the new method.


Searching for records based on indexed fields

Section topics 	See also

Table components continue to support a set of Goto search methods for backward compatibility. Goto methods enable you to search for a record based on indexed fields, referred to as a key, and make the first record found the new current record.

For Paradox and dBASE tables, the key must always be an index, which you can specify in a table component's IndexName property. For SQL tables, the key can also be a list of fields you specify in the IndexFieldNames property. You can also specify a field list for Paradox or dBASE tables, but the fields must have indexes defined on them. For more information about IndexName and IndexFieldNames, see "Searching on alternate indexes."

Tip:	To search on nonindexed fields in a Paradox or dBASE table, use Locate. Alternatively, you can use a TQuery component and a SELECT statement to search on nonindexed fields in Paradox and dBASE fields. For more information about TQuery, see "Working with queries."

The following table summarizes the six related Goto and Find methods an application can use to search for a record:

Method	Purpose
EditKey	Preservers the current contents of the search key buffer and puts the table into dsSetKey state so your application can modify existing search criteria prior to executing a search.
FindKey	Combines the SetKey and GotoKey methods in a single method.
FindNearest	Combines the SetKey and GotoNearest methods in a single method.
GotoKey	Searches for the first records in a dataset that exactly matches the search criteria, and moves the cursor to that record if one is found.
GotoNearest	Searches on string-based fields for the closest match to a record based on partial key values, and moves the cursor to that record.
SetKey	Clears the search key buffer and puts the table into dsSetKey state so your application can specify new search criteria prior to executing a search.

GotoKey and FindKey are Boolean functions that, if successful, move the cursor to a matching record and return True. If the search is unsuccessful, the cursor is not moved, and these functions return False.

GotoNearest and FindNearest always reposition the cursor either on the first exact match found or, if no match is found, on the first record that is greater than the specified search criteria.

Executing a search with Goto methods

To execute a search using Goto methods:

1.	Specify the index to use for the search in the IndexName property, if necessary. (For SQL tables, list the fields to use as a key in IndexFieldNames instead.) If you use a table's primary index, you do not need to set these properties.

2.	Open the table.

3.	Put the table in dsSetKey state with SetKey.

4.	Specify the value(s) to search on in the Fields property. Fields is a string list that you index into with ordinal numbers corresponding to columns. The first column number in a table is 0.

5.	Search for and move to the first matching record found with GotoKey or GotoNearest.

For example, the following code, attached to a button's OnClick event, moves to the first record containing a field value that exactly matches the text in an edit box on a form:

procedure TSearchDemo.SearchExactClick(Sender: TObject);

begin

 Table1.SetKey;

 Table1.Fields[0].AsString := Edit1.Text;

 if not Table1.GotoKey then

   ShowMessage('Record not found');

end;

GotoNearest is similar. It searches for the nearest match to a partial field value. It can be used only for string fields. For example,

Table1.SetKey;

Table1.Fields[0].AsString := 'Sm';

Table1.GotoNearest;

If a record exists with "Sm" as the first two characters, the cursor is positioned on that record. Otherwise, the position of the cursor does not change and GotoNearest returns False.

Executing a search with Find methods

To execute a search using Find methods:

1.	Specify the index to use for the search in the IndexName property, if necessary. (For SQL tables, list the fields to use as a key in IndexFieldNames instead.) If you use a table's primary index, you do not need to set these properties.

2.	Open the table.

3.	Search for and move to the first or nearest record with FindKey or FindNearest. Both methods take a single parameter, a comma-delimited list of field values, where each value corresponds to an indexed column in the underlying table.

Note:	FindNearest can be used only for string fields.


Specifying the current record after a successful search

Section topics 	See also

By default, a successful search positions the cursor on the first record that matches the search criteria. If you prefer, you can set the KeyExclusive property for a table component to True to position the cursor on the next record after the first matching record.

By default, KeyExclusive is False, meaning that successful searches position the cursor on the first matching record.


Searching on partial keys

Section topics 	See also

If a table has more than one key column, and you want to search for values in a sub-set of that key, set KeyFieldCount to the number of columns on which you are searching. For example, if a table has a three column primary key, and you want to search for values in just the first column, set KeyFieldCount to 1.

For tables with multiple-column keys, you can search only for values in contiguous columns, beginning with the first. For example, for a three-column key you can search for values in the first column, the first and second, or the first, second, and third, but not just the first and third.


Searching on alternate indexes

Section topics 	See also

If you want to search on an index other than the primary index for a table, then you must specify the name of the index to use in the IndexName property for the table. A table must be closed when you specify a value for IndexName. For example, if the CUSTOMER table had a secondary index named "CityIndex" on which you wanted to search for a value, you need to set the value of the table's IndexName property to "CityIndex":

Table1.Close;

Table1.IndexName := 'CityIndex';

Table1.Open;

Table1.SetKey;

Table1['City'] := Edit1.Text;

Table1.GotoNearest;

Instead of specifying an index name, you can list fields to use as a key in the IndexFieldNames property. For Paradox and dBASE tables, the fields you list must be indexed, or an exception is raised when you execute the search. For SQL tables, the fields you list need not be indexed.


Repeating or extending a search

Section topics 	See also

Each time you call SetKey or FindKey it clears any previous values in the Fields property. If you want to repeat a search using previously set fields, or you want to add to the fields used in a search, call EditKey in place of SetKey and FindKey. For example, to extend the above search to find a record with a specified city name in a specified country, use the following code:

Table1.EditKey;

Table1['Country'] := Edit2.Text;

Table1.GotoNearest;


Sorting records

Section topics 	See also

An index determines the display order of records in a table. In general, Delphi displays records in ascending order based on a primary index (for dBASE tables without a primary index, sort order is based on physical record order). This default behavior does not require application intervention. If you want a different sort order, however, you must specify either

An alternate index.

A list of columns on which to sort (SQL only).

Specifying a different sort order requires the following steps:

1.	Determining available indexes.

2.	Specifying the alternate index or column list to use.


Retrieving a list of available indexes with GetIndexNames

Section topics 	See also

At run time, your application can call the GetIndexNames method to retrieve a list of available indexes for a table. GetIndexNames returns a string list containing valid index names. For example, the following code determines the list of indexes available for the CustomersTable dataset:

var

 IndexList: TList;

...

CustomersTable.GetIndexNames(IndexList);

Note:	For Paradox tables, the primary index is unnamed, and is therefore not returned by GetIndexNames. If you need to return to using a primary index on a Paradox table after using an alternative index, set the table's IndexName property to a null string.


Specifying an alternative index with IndexName

Section topics 	See also

To specify that a table should be sorted using an alternative index, specify the index name in the table component's IndexName property. At design time you can specify this name in the Object Inspector, and at run time you can access the property in your code. For example, the following code sets the index for CustomersTable to CustDescending:

CustomersTable.IndexName := 'CustDescending';

Specifying a dBASE index file

For dBASE tables that use non-production indexes, set the IndexFiles property to the name of the index file(s) to use before you set IndexName. At design time you can click the ellipsis button in the IndexFiles property value in the Object Inspector to invoke the Index Files editor.

To see a list of available index files, choose Add, and select one or more index files from the list. A dBASE index file can contain multiple indexes. To select an index from the index file, select the index name from the IndexName drop-down list in the Object Inspector. You can also specify multiple indexes in the file by entering desired index names, separated by semicolons.

You can also set IndexFiles and IndexName at run time. For example, the following code sets the IndexFiles for the AnimalsTable table component to ANIMALS.MDX, and then sets IndexName to NAME:

AnimalsTable.IndexFiles := 'ANIMALS.MDX';

AnimalsTable.IndexName := 'NAME';


Specifying sort order for SQL tables

Section topics 	See also

In SQL, sort order of rows is determined by the ORDER BY clause. You can specify the index used by this clause either with the

IndexName property, to specify an existing index, or

IndexFieldNames property, to create a pseudo-index based on a subset of columns in the table

IndexName and IndexFieldNames are mutually exclusive. Setting one property clears values set for the other. To use IndexName, see "Searching on alternate indexes."

Specifying fields with IndexFieldNames

IndexFieldNames is a string list property. To specify a sort order, list each column name to use in the order it should be used, and delimit the names with semicolons. Sorting is by ascending order only. Case-sensitivity of the sort depends on the capabilities of your server. See your server documentation for more information.

The following code sets the sort order for PhoneTable based on LastName, then FirstName:

PhoneTable.IndexFieldNames := 'LastName;FirstName';

Note:	If you use IndexFieldNames on Paradox and dBASE tables, Delphi attempts to find an index that uses the columns you specify. If it cannot find such an index, it raises an exception.


Examining the field list for an index

Section topics 	See also

When your application uses an index at run time, it can examine the

IndexFieldCount property, to determine the number of columns in the index.

IndexFields property, to examine a list of column names that comprise the index.

IndexFields is a string list containing the column names for the index. The following code fragment illustrates how you might use IndexFieldCount and IndexFields to iterate through a list of column names in an application:

var

 I: Integer;

 ListOfIndexFields: array[0 to 20} of string;

begin

with CustomersTable do

 begin

 for I := 0 to IndexFieldCount - 1 do

    ListOfIndexFields[I] := IndexFields[I];

 end;

end;

Note:	IndexFieldCount is not valid for a dBASE table opened on an expression index.


Working with a subset of data

Section topics 	See also

Production tables can be huge, so applications often need to limit the number of rows with which they work. For table components, there are two ways to limit records used by an application: ranges and filters. Filters can be used with any kind of dataset, including TTable, TQuery, and TStoredProc components. Because they apply to all datasets, you can find a full discussion of using filters in "Understanding datasets."

Ranges only apply to TTable components. Despite their similarities, ranges and filters have different uses. The following section discusses the differences between ranges and filters and then discusses how to use ranges.


Understanding the differences between ranges and filters

Section topics 	See also

Both ranges and filters restrict visible records in a table to a subset of all available records, but the way they do so differs. A range is a set of contiguously indexed records that fall between specified boundary values. For example, in an employee database indexed on last name, you might apply a range to display all employees whose last names are greater than "Jones" and less than "Smith". Because ranges depend on indexes, ranges can be applied only to indexed Paradox and dBASE tables (for SQL tables, ranges can be applied to any fields you list in the IndexFieldNames property). Ranges must be ordered based on existing indexes.

A filter, on the other hand, is any set of contiguous and noncontiguous records that share specified data points, regardless of indexing. For example, you might filter an employee database to display all employees who live in California and who have worked for the company for five or more years. While filters make use of indexes if they apply, filters are not dependent on them. Filters are applied record-by-record as an application scrolls through a dataset.

In general, filters are more flexible than ranges. Ranges, however, can be more efficient when datasets are very large and the records of interest to an application are already blocked in contiguously indexed groups. For very large datasets, it may be still more efficient to use a query component to select data for viewing and editing. For more information about using filters, see "Understanding datasets." For more information about using queries, see "Working with queries."


Creating and applying a new range

Section topics 	See also

The process for creating and applying a range involves these general steps:

1.	Putting the dataset into dsSetKey state and setting the starting index value for the range.

2.	Setting the ending index value for the range.

3.	Applying the range to the dataset.

Setting the beginning of a range

Call the SetRangeStart procedure to put the dataset into dsSetKey state and begin creating a list of starting values for the range. Once you call SetRangeStart, subsequent assignments to the Fields property are treated by Delphi as starting index values to use when applying the range. Fields specified must be indexed fields when using Paradox and dBASE.

For example, suppose your application uses a table component named Customers, linked to the CUSTOMER table, and that you have created persistent field components for each field in the Customers dataset. CUSTOMER is indexed on its first column (CustNo). A form in the application form has two edit components named StartVal and EndVal, used to specify start and ending values for a range. If so, the following code could be used to create and apply a range:

with Customers do

begin

 SetRangeStart;

 FieldByName('CustNo') := StartVal.Text;

 SetRangeEnd;

 if EndVal.Text <> '' then

   FieldByName('CustNo') := EndVal.Text;

 ApplyRange;

end

This code checks that the text entered in EndVal is not null before assigning any values to Fields. If the text entered for StartVal is null, then all records from the beginning of the table will be included, since all values are greater than null. However, if the text entered for EndVal is null, then no records will be included, since none are less than null.

For a multi-column index, you can specify a starting value for all or some fields in the index. If you do not supply a value for a field used in the index, a null value is assumed when you apply the range. If you set more values than there are fields in the index, the extra fields are ignored when computing the range.

To finish specifying the start of a range, call SetRangeEnd or ApplyRange. These methods are discussed in the following sections.

Tip:	To start at the beginning of the dataset, omit the call to SetRangeStart.

You can also set the starting (and ending) values for a range with a call to the SetRange procedure. For more information about SetRange, see "Setting start- and end-range values."

Setting the end of a range

Call the SetRangeEnd procedure to put the dataset into dsSetKey state and start creating a list of ending values for the range. Once you call SetRangeEnd, subsequent assignments to the Fields property are treated by Delphi as ending index values to use when applying the range. Fields specified must be indexed fields when using Paradox and dBASE.

Note:	Always specify the ending values for a range, even if you want a range to end on the last record in the dataset. If you do not provide ending values, Delphi assumes the ending value of the range is a null value. A range with null ending values is always empty.

The easiest way to assign ending values is to call the FieldByName method. For example,

with Table1 do

begin

 SetRangeStart;

 FieldByName('LastName') := Edit1.Text;

 SetRangeEnd;

 FieldByName('LastName') := Edit2.Text;

 ApplyRange;

end;

For a multi-column index, you can specify a starting value for all or some fields in the index. If you do not supply a value for a field used in the index, a null value is assumed when you apply the range. If you set more values than there are fields in the index, an exception is raised.

To finish specifying the end of a range, call ApplyRange. For more information about applying a range, see "Applying a range."

Note:	You can also set the ending (and starting) values for a range with a call to the SetRange procedure. For more information about SetRange, see "Setting start- and end-range values."

Setting start- and end-range values

Instead of using separate calls to SetRangeStart and SetRangeEnd to specify range boundaries, you can call the SetRange procedure to put the dataset into dsSetKey state and set the starting and ending values for a range with a single call.

SetRange takes two constant array parameters: a set of starting values, and a set of ending values. For example, the following statements establish a range based on a two-column index:

 SetRange([Edit1.Text, Edit2.Text], [Edit3.Text, Edit4.Text]);

For a multi-column index, you can specify starting and ending values for all or some fields in the index. If you do not supply a value for a field used in the index, a null value is assumed when you apply the range. To omit a value for the first field in an index, and specify values for successive fields, pass a nil or empty value for the omitted field. If you set more values than there are fields in the index, the extra fields are ignored when computing the range.

Note:	Always specify the ending values for a range, even if you want a range to end on the last record in the dataset. If you do not provide ending values, Delphi assumes the ending value of the range is a null value. A range with null ending values is always empty because the starting range is greater than or equal to the ending range.

Specifying a range based on partial keys

If a key is composed of one or more string fields, the SetRange methods support partial keys. For example, if an index is based on the LastName and FirstName columns, the following range specifications are valid:

Table1.SetRangeStart;

Table1['LastName'] := 'Smith';

Table1.SetRangeEnd;

Table1['LastName'] := 'Zzzzzz';

Table1.ApplyRange;

This code includes all records in a range where LastName is greater than or equal to "Smith." The value specification could also be:

Table1['LastName'] := 'Sm';

This statement includes records that have LastName greater than or equal to "Sm." The following statement includes records with a LastName greater than or equal to "Smith" and a FirstName greater than or equal to "J":

Table1['LastName'] := 'Smith';

Table1['FirstName'] := 'J';

Including or excluding records that match boundary values

By default, a range includes all records that are greater than or equal to the specified starting range, and less than or equal to the specified ending range. This behavior is controlled by the KeyExclusive property. KeyExclusive is False by default.

If you prefer, you can set the KeyExclusive property for a table component to True to exclude records equal to the specified starting and ending ranges. For example,

KeyExclusive := True;

Table1.SetRangeStart;

Table1['LastName'] := 'Smith';

Table1.SetRangeEnd;

Table1['LastName'] := 'Tyler';

Table1.ApplyRange;

This code includes all records in a range where LastName is greater than "Smith" and less than "Tyler".

Applying a range

The SetRange methods described in the preceding sections establish the boundary conditions for a range, but do not put it into effect. To make a range take effect, call the ApplyRange procedure. ApplyRange immediately restricts a user's view of and access to data in the specified subset of the dataset.

Canceling a range

The CancelRange method ends application of a range and restores access to the full dataset. Even though canceling a range restores access to all records in the dataset, the boundary conditions for that range are still available so that you can reapply the range at a later time. Range boundaries are preserved until you provide new range boundaries or modify the existing boundaries. For example, the following code is valid:

.

.

.

Table1.CancelRange;

.

.

.

{later on, use the same range again. No need to call SetRangeStart, etc.}

Table1.ApplyRange;

.

.

.


Modifying a range

Section topics 	See also

Delphi provides two functions that enable you to modify the existing boundary conditions for a range: EditRangeStart, for changing the starting values for a range; and EditRangeEnd, for changing the ending values for the range.

The process for editing and applying a range involves these general steps:

1.	Putting the dataset into dsSetKey state and modifying the starting index value for the range.

2.	Modifying the ending index value for the range.

3.	Applying the range to the dataset.

You can modify either the starting or ending values of the range, or you can modify both boundary conditions. If you modify the boundary conditions for a range that is currently applied to the dataset, the changes you make are not applied until you call ApplyRange again.

Editing the start of a range

Call the EditRangeStart procedure to put the dataset into dsSetKey state and begin modifying the current list of starting values for the range. Once you call EditRangeStart, subsequent assignments to the Fields property overwrite the current index values to use when applying the range. Fields specified must be indexed fields when using Paradox and dBASE.

Tip:	If you initially created a start range based on a partial key, you can use EditRangeStart to extend the starting value for a range. For more information about ranges based on partial keys, see "Specifying a range based on partial keys."

Editing the end of a range

Call the EditRangeEnd procedure to put the dataset into dsSetKey state and start creating a list of ending values for the range. Once you call EditRangeEnd, subsequent assignments to the Fields property are treated by Delphi as ending index values to use when applying the range. Fields specified must be indexed fields when using Paradox and dBASE.

Note:	Always specify the ending values for a range, even if you want a range to end on the last record in the dataset. If you do not provide ending values, Delphi assumes the ending value of the range is a null value. A range with null ending values is always empty.


Deleting all records in a table

Section topics 	See also

To delete all rows of data in a table, call a table component's EmptyTable method at run time. For SQL tables, this method succeeds only if you have DELETE privilege for the table. For example, the following statement deletes all records in a dataset:

PhoneTable.EmptyTable;

Caution:	Data you delete with EmptyTable is gone forever.


Deleting a table

Section topics 	See also

To delete a table from a database, call a table component's DeleteTable method. For example, the following statement removes the table underlying a dataset:

CustomersTable.DeleteTable;

Caution:	When you delete a table with DeleteTable, the table and all its data are gone forever.


Creating a table

Section topics 	See also

The CreateTable method enables an application to create a new table at run time. To use this method, an application must instantiate a table component at run time, assign field and index definitions to the table, and then call CreateTable to generate the specified table. CreateTable provides a programmatic way to create tables without requiring SQL knowledge. It does, however, require you to be intimately familiar with the properties, events, and methods common to Delphi dataset components, TTable in particular.

Warning:	If you create a table that duplicates the name of an existing table, the existing table and all its data are overwritten by the newly created table. The old table and its data cannot be recovered.

To use CreateTable, an application must:

1.	Declare and instantiate a table component.

2.	Specify the DatabaseName and TableName properties for the table component.

3.	Set the TableType property to ttDefault or another value.

4.	Clear any existing field definitions for the table.

5.	Add new field definitions to the FieldDefs property.

6.	Clear any existing index definitions.

7.	Specify index options for the table.

8.	Optionally add one or more indexes to the table.

9.	Call CreateTable.

For example, the following code creates a new table and associates it with the DBDEMOS alias, but before it creates the new table, it verifies that the table name provided does not match the name of an existing table:

var

 NewTable: TTable;

 NewIndexOptions: TIndexOptions;

 TableFound: Boolean;

begin

 NewTable := TTable.Create;

 NewIndexOptions := [ixPrimary, ixUnique];

 with NewTable do

 begin

   Active := False;

   DatabaseName := 'DBDEMOS';

   TableName := Edit1.Text;

   TableType := ttDefault;

   FieldDefs.Clear;

   FieldDefs.Add(Edit2.Text, ftInteger, 0, False);

   FieldDefs.Add(Edit3.Text, ftInteger, 0, False);

   IndexDefs.Clear;

   IndexDefs.Add('PrimaryIndex', Edit2.Text, NewIndexOptions);

 end;

 {Now check for prior existence of this table}

 TableFound := FindTable(Edit1.Text); {code for FindTable not shown}

 if TableFound = True then

  if MessageDlg('Overwrite existing table ' + Edit1.Text + '?', mtConfirmation,
      mbYesNo, 0) = mrYes then

   TableFound := False;

 if not TableFound then

   CreateTable; { create the table}

 end;

end;


Importing data from another table

Section topics 	See also

You can use a table component's BatchMove method to import data from another table. BatchMove can

Copy records from another table into this table.

Update records in this table that occur in another table.

Append records from another table to the end of this table.

Delete records in this table that occur in another table.

BatchMove takes two parameters: the name of the table from which to import data, and a mode specification that determines which import operation to perform. The following table describes the possible settings for the mode specification:

Value	Meaning
batAppend	Append all records from the source table to the end of this table.
batAppendUpdate	Append all records from the source table to the end of this table and update existing records in this table with the same records from the source table.
batCopy	Copy all records from the source table into this table.
batDelete	Delete all records in this table that also appear in the source table.
batUpdate	Update existing records in this table with their counterparts in the source table.

For example, the following code updates records in the current table with records from the Customer table:

Table1.BatchMove('CUSTOMER.DB', batUpdate);

BatchMove returns the number of records it imports successfully.

Caution:	Importing records using the batCopy mode overwrites existing records. To preserve existing records use batAppend instead.

BatchMove performs only some of the functions available to your application directly through the TBatchMove component. If you need to move a large amount of data between or among tables, use the batch move component instead of calling a table's BatchMove function. For more information about using TBatchMove, see "Copying data and table structures."


Synchronizing tables linked to the same database table

Section topics 	See also

If more than one table component is linked to the same database table through their DatabaseName and TableName properties and the tables do not share a data source component, then each table has its own view on the data and its own current record. As users access records through each table component, the components' current records will differ.

You can force the current record for each of these table components to be the same with the GotoCurrent method. GotoCurrent sets its own table's current record to the current record of another table component. For example, the following code sets the current record of CustomerTableOne to be the same as the current record of CustomerTableTwo:

CustomerTableOne.GotoCurrent(CustomerTableTwo);

Tip:	If your application needs to synchronize table components in this manner, put the components in a data module and add the data module to the uses clause of each form that accesses the tables.

If you must synchronize table components on separate forms, you must add one form to the uses clause of the other form, and you must qualify at least one of the table names with its form name. For example:

CustomerTableOne.GotoCurrent(Form2.CustomerTableTwo);


Creating master/detail forms

Section topics 	See also

A table component's MasterSource and MasterFields properties can be used to establish one-to-many relationships between two tables.

The MasterSource property is used to specify a data source from which the table will
get data for the master table. For instance, if you link two tables in a master/detail relationship, then the detail table can track the events occurring in the master table by specifying the master table's data source component in this property.

The MasterFields property specifies the column(s) common to both tables used to establish the link. To link tables based on multiple column names, use a semicolon delimited list:

Table1.MasterFields := 'OrderNo;ItemNo';

To help create meaningful links between two tables, you can use the Field Link designer.


Building an example master/detail form
Building a sample master/detail form

Section topics 	See also

To create a simple form in which a user can scroll through customer records and display all orders for the current customer:
(Note: The master table is the CustomersTable table, and the detail table is OrdersTable.)

1.	Place two TTable and two TDataSource components in a data module.

2.	Set the properties of the first TTable component as follows:

DatabaseName: DBDEMOS

TableName: CUSTOMER

Name: CustomersTable

3.	Set the properties of the second TTable component as follows:

DatabaseName: DBDEMOS

TableName: ORDERS

Name: OrdersTable

4.	Set the properties of the first TDataSource component as follows:

Name: CustomersSource

DataSet: CustomersTable

5.	Set the properties of the second TDataSource component as follows:

Name: OrdersSource

DataSet: OrdersTable

6.	Place two TDBGrid components on a form.

7.	Choose File|Use Unit to specify that the form should use the data module.

8.	Set the DataSource property of the first grid component to "CustomersSource", and set the DataSource property of the second grid to "OrdersSource".

9.	Set the MasterSource property of OrdersTable to "CustomersSource". This step links the CUSTOMER table (the master, or control, table) to the ORDERS table (the detail table).

10.	Double-click the MasterFields property value box in the Object Inspector to invoke the Field Link Designer to set the following properties:

In the Available Indexes field, choose CustNo to link the two tables by the CustNo field.

Select CustNo in both the Detail Fields and Master Fields field lists.

Click the Add button to add this join condition. In the Joined Fields list,
"CustNo -> CustNo" appears.

Choose OK to commit your selections and exit the Field Link Designer.

11.	Set the Active properties of CustomersTable and OrdersTable to True to display data in the grids on the form.

12.	Compile and run the application.

When you run the application, the tables are linked. When you move to a new record in the grid displaying customer records, the grid that displays order records changes to display only those orders associated with the current customer in the customer grid.

If you run the application now, you will see that the tables are linked together, and that when you move to a new record in the CUSTOMER table, you see only those records in the ORDERS table that belong to the current customer.


See also

TTable

TDataSet

Understanding datasets

BDE-enabled datasets


Working with queries

Section topics 	See also

These topics describe the TQuery dataset component which enables you to use SQL statements to access data. It assumes you are familiar with the general discussion of datasets and data sources in "Understanding datasets."

A query component encapsulates an SQL statement that is used in a client application to retrieve, insert, update, and delete data from one or more database tables. SQL is an industry-standard relational database language that is used by most remote, server-based databases, such as Sybase, Oracle, InterBase, and Microsoft SQL Server. Delphi query components can be used with remote database servers (Delphi Client/Server and Enterprise only), with Paradox, dBASE, FoxPro, and Access, and with OBDC-compliant databases.


Using queries effectively

Section topics 	See also

To use the query component effectively, you must be familiar with

SQL and your server's SQL implementation, including limitations and extensions to the SQL-92 standard.

The Borland Database Engine (BDE).

If you are an experienced desktop database developer moving to server-based applications, see "Queries for desktop developers" for an introduction to queries before reading the rest of these topics. If you are new to SQL, you may want to purchase one of the many fine third party books that cover SQL in-depth. One of the best is Understanding the New SQL: A Complete Guide, by Jim Melton and Alan R. Simpson, Morgan Kaufmann Publishers.

If you are an experienced database server developer, but are new to building Delphi clients, then you are already familiar with SQL and your server, but you may be unfamiliar with the BDE. See "Queries for server developers" for an introduction to queries and the BDE before reading the rest of these topics.


Queries for desktop developers

Section topics 	See also

As a desktop developer you are already familiar with the basic table, record, and field paradigm used by Delphi and the BDE. You feel very comfortable using a TTable component to gain access to every field in every data record in a dataset. You know that when you set a table's TableName property, you specify the database table to access.

Chances are you have also used a TTable's range and filter properties and methods to limit the number of records available at any given time in your applications. Applying a range temporarily limits data access to a block of contiguously indexed records that fall within prescribed boundary conditions, such as returning all records for employees whose last names are greater than or equal to "Jones" and less than or equal to "Smith". Setting a filter temporarily restricts data access to a set of records that is usually noncontiguous and that meets filter criteria, such as returning only those customer records that have a California mailing address.

A query behaves in many ways very much like a table filter, except that you use the query component's SQL property (and sometime the Params property, too) to identify the records in a dataset to retrieve, insert, delete, or update. In some way a query is even more powerful than a filter because it lets you access

More than one table at a time (called a "join" in SQL).

A specified subset of rows and columns in its underlying table(s), rather than always returning all rows and columns. This improves both performance and security. Memory is not wasted on unnecessary data, and you can prevent access to fields a user should not view or modify.

Queries can be verbatim, or they can contain replaceable parameters. Queries that use parameters are called parameterized queries. When you use parameterized queries, the actual values assigned to the parameters are inserted into the query by the BDE before you execute, or run, the query. Using parameterized queries is very flexible, because you can change a user's view of and access to data on the fly at run time.

Most often you use queries to select the data that a user should see in your application, just as you do when you use a table component. Queries, however, can also perform update, insert, and delete operations instead of retrieving records for display. When you use a query to perform insert, update, and delete operations, the query ordinarily does not return records for viewing. In this way a query differs from a table.

To learn more about using the SQL property to write an SQL statement, see "Specifying the SQL statement to execute." To learn more about using parameters in your SQL statements, see "Setting parameters." To learn about executing a query, see "Executing a query."


Queries for server developers

Section topics 	See also

As a server developer you are already familiar with SQL and with the capabilities of your database server. To you a query is the SQL statement you use to access data. You know how to use and manipulate this statement and how to use optional parameters with it.

The SQL statement and its parameters are the most important parts of a query component. The query component's SQL property is used to provide the SQL statement to use for data access, and the component's Params property is an optional array of parameters to bind into the query. In Delphi, however, a query component is much more than an SQL statement and its parameters. A query component is also the interface between your client application and the BDE.

A client application uses the properties and methods of a query component to manipulate an SQL statement and its parameters, to specify the database to query, to prepare and unprepare queries with parameters, and to execute the query. A query component's methods call the BDE, which, in turn, processes your query requests, and communicates with the database server, usually through an SQL Links driver for Delphi Client/Server and Delphi Enterprise. The server passes a result set, if appropriate, back to the BDE, and the BDE returns it to your application through the query component.

When you work with a query component, you should be aware that some of the terminology used to describe BDE features can be confusing at first because it has very different meanings to you as an SQL database programmer. For example, the BDE commonly uses the term "alias" to refer to a shorthand name for the path to the database server. The BDE alias is stored in a configuration file, and is set in the query component's DatabaseName property. (You can still use table aliases in your SQL statements.)

Similarly, the BDE help documentation, available online in \Borland\Common Files\BDE32.HLP, often refers to queries that use parameters as "parameterized queries" where you are more likely to think of SQL statements that use bound variables or parameter bindings.

Note:	These topics uses BDE terminology because you will encounter it throughout Borland's documentation. Whenever confusion may result from using BDE terms, explanations are provided.

To learn more about using the SQL property to write an SQL statement, see "Specifying the SQL statement to execute." To learn more about using parameters in your SQL statements, see "Setting parameters." To learn about preparing a query, see "Preparing a query." and to learn more about executing a query, see "Executing a query."


What databases can you access with a query component?

Section topics 	See also

A TQuery component can access data in

Paradox or dBASE tables, using Local SQL, which is part of the BDE. Local SQL supports the SQL-92 standard with the exception of some DDL syntax.

Local InterBase Server databases, using the InterBase engine. For information on InterBase's SQL-92 standard SQL syntax support and extended syntax support, see the InterBase Language Reference.

Databases on remote database servers such as Oracle, Sybase, MS-SQL Server, Informix, DB2, and InterBase (Delphi Client/Server only). You must have installed the appropriate SQL Link to access a remote server. Any standard SQL syntax supported by these server is allowed. For information on SQL syntax, limitations, and extensions, see your server documentation.

Delphi also supports heterogeneous queries against more than one server or table type (for example, data from an Oracle table and a Paradox table). When you create a heterogeneous query, the BDE uses Local SQL to process the query. For more information, see "Creating heterogenous queries."


Using a query component: an overview

Section topics 	See also

To use a query component at design time in an application:

1.	Place a query component from the Data Access tab of the Component palette in a data module, and set its Name property appropriately for your application.

2.	Set the DatabaseName property of the component to the name of the database to query. DatabaseName can be a BDE alias or, for local database access, an explicit directory path and database name. If your application uses a database component, then you can set DatabaseName to the name of a local BDE alias defined in that database component's DatabaseName property.

3.	Specify an SQL statement in the SQL property of the component, and optionally specify any parameters for the statement in the Params property. For more information, see "Specifying the SQL property at design time."

4.	Place a data source component from the Data Access tab of the Component palette in the data module, and set its DataSet property to the name of the query component. The data source module is used to return the results of the query (called a result set) from the query to data-aware components for display.

To execute a query for the first time at run time:

1.	Close the query component.

2.	Provide an SQL statement in the SQL property either because you did not set the SQL property at design time, or because you want to change the SQL statement already provided. To use the design-time statement as is, skip this step. For more information about setting the SQL property, see "Specifying the SQL statement to execute."

3.	Set parameters and parameter values in the Params property either directly or by using the ParamByName method. If a query does not contain parameters, or the parameters set at design time are unchanged, skip this step. For more information about setting parameters, see "Setting parameters."

4.	Call Prepare to initialize the BDE and bind parameter values into the query. Calling Prepare is optional, though highly recommended. For more information about preparing a query, see "Preparing a query."

5.	Call Open for queries that return a result set, or call ExecSQL for queries that do not return a result set. For more information about opening and executing a query see "Executing a query."

After you execute a query for the first time, then as long as you do not modify the SQL statement, an application can repeatedly close and reopen or reexecute a query without repreparing it. For more information about reusing a query, see "Executing a query."


Specifying the SQL statement to execute

Section topics 	See also

Use the SQL property to specify the SQL query statement to execute. At design time a query is prepared and executed automatically when you set the query component's Active property to True. At run time, a query is prepared with a call to Prepare, and executed when the application calls the component's Open or ExecSQL methods.

The SQL property is a TString object, which is an array of text strings and a set of properties, events, and methods that manipulate them. The strings in SQL are automatically concatenated to produce the SQL statement to execute. You can provide a statement in as few or as many separate strings as you desire. One advantage to using a series of strings is that you can divide the SQL statement into logical units (for example, putting the WHERE clause for a SELECT statement into its own string), so that it is easier to modify and debug a query.

The SQL statement can be a query that contains hard-coded field names and values, or it can be a parameterized query that contains replaceable parameters that represent field names and values that must be bound into the statement before it is executed. For example, this statement is hard-coded:

SELECT * FROM CUSTOMER WHERE CUSTNO = 1231

Hard-coded statements are useful when applications execute exact, known queries each time they run. At design time or runtime you can easily replace one hard-code query with another hard-coded or parameterized query as needed. Whenever the SQL property is changed the query is automatically closed and unprepared.

Note:	In queries made against the BDE engine using local SQL, when column names in a query contain spaces or special characters, the column name must be enclosed in quotes and must be preceded by a table reference and a period. For example, BIOLIFE."Species Name". For information about valid column names, see your server's documentation.

A parameterized query contains one or more placeholder parameters, application variables that stand in for field names or comparison values such as those found in the WHERE clause of a SELECT statement. Using parameterized queries enables you to change the value without rewriting the application. Parameter values must be bound into the SQL statement before it is executed for the first time. Delphi does this automatically for you if you do not explicitly call the Prepare method before executing a query.

This statement is a parameterized query:

SELECT * FROM CUSTOMER WHERE CUSTNO = :Number

The variable Number, indicated by the leading colon, is a parameter that fills in for a comparison value that must be provided at run time and that may vary each time the statement is executed. The actual value for Number is provided in the query component's Params property.

Tip:	It is a good programming practice to provide variable names for parameters that correspond to the actual name of the column with which it is associated. For example, if a column name is "Number," then its corresponding parameter would be ":Number". Using matching names ensures that if a query uses its DataSource property to provide values for parameters, it can match the variable name to valid field names.


Specifying the SQL property at design time

Section topics 	See also

Specify the SQL property at design time by using the String List editor. To invoke the String List editor for the SQL property

Double-click on the SQL property value column, or

Click its ellipsis button.

You can enter an SQL statement in as many or as few lines as you want. Entering a statement on multiple lines, however, makes it easier to read, change, and debug. Choose OK to assign the text you enter to the SQL property.

Normally, the SQL property can contain only one complete SQL statement at a time, although these statements can be as complex as necessary (for example, a SELECT statement with a WHERE clause that uses several nested logical operators such as AND and OR). Some servers also support "batch" syntax that permits multiple statements; if your server supports such syntax, you can enter multiple statements in the SQL property.

Note:	With Delphi Client/Server Suite, you can also use the Visual Query Builder to construct a query based on a visible representation of tables and fields in a database. To use the Visual Query Builder, select a query component, right-click it to invoke the context menu, and choose Query Builder. To learn how to use the Query Builder, open it and see to its online help.


Specifying an SQL statement at run time

Section topics 	See also

There are two ways to set the SQL property at run time. An application can set the SQL property directly, or it can call the SQL property's LoadFromFile method to read an SQL statement from a file.

Setting the SQL property directly

To set the SQL property at run time:

1.	Call Close to inactive the query. It is always safe to call Close even for queries that are currently inactive, and you should call Close so that you can reopen or reexecute the query with its new SQL statement.

2.	Call the Clear method for the SQL property to delete its current SQL statement.

3.	Call the Add method for the SQL property to insert and append one or more strings to the SQL property to create a new SQL statement.

4.	Call Open or ExecSQL to execute the query.

The following code illustrates these steps:

CustomerQuery.Close; // Close the query if it's active

CustomerQuery.SQL.Clear; // Delete the current SQL statement, if there is one

CustomerQuery.SQL.Add('SELECT * FROM ORDERS');

CustomerQuery.SQL.Add('WHERE COMPANY = ''Sight Diver''');

CustomerQuery.Open;

Note:	If a query uses parameters, you should also set their initial values and call the Prepare method before opening or executing a query.

Loading the SQL property from a file

You can also use the LoadFromFile method to assign an SQL statement in a text file to the SQL property. For example:

CustomerQuery.Close;

CustomerQuery.SQL.Clear;

CustomerQuery.SQL.LoadFromFile('C:\ORDERS.TXT');

CustomerQuery.Open;

Note:	If the SQL statement contained in the file is a parameterized query, set the initial values for the parameters and call Prepare before opening or executing the query.


Setting parameters

Section topics 	See also

A parameterized SQL statement contains parameters, or variables, the values of which can be varied at design time or run time. Parameters can replace column names or data values, such as those used in a WHERE clause for comparisons, that appear in an SQL statement. Ordinarily, parameters stand in for data values passed to the statement. For example, in the following INSERT statement, values to insert are passed as parameters:

INSERT INTO COUNTRY (NAME, CAPITAL, POPULATION)

 VALUES (:name, :capital, :population)

In this SQL statement, :name, :capital, and :population are placeholders for actual values supplied to the statement at run time by your application. Before a parameterized query is executed for the first time, your application should call the Prepare method to bind the current values for the parameters to the SQL statement. Binding means that the BDE and the server allocate resources for the statement and its parameters that improve the execution speed of the query.


Supplying parameters at design time

Section topics 	See also

At design time, the easiest and safest way to enter query parameters is to invoke the Query Parameters editor. The Query Parameters editor lists parameters in the correct order and lets you assign values to them. When you define parameters at design time, your query is automatically reprepared for you.

To invoke the Query Parameters editor:

1.	Select the query component.

2.	Right-click the component to invoke the context menu.

3.	Choose Define Parameters.

For queries without previously defined parameters, the Parameter name list box is empty. If parameters are already defined for a query, then the parameter names are displayed in the list box.

The Data type combo box lists the BDE data type for a parameter selected in the list box. You must set a data type for each parameter. In general, BDE data types conform to server data types. For specific BDE-to-server data type mappings, see the BDE help in \Borland\Common Files\BDE32.HLP.

The Value edit box enables you to enter a value for a selected parameter, and the Null Value check box enables you set to a NULL value for the selected parameter if its data type permits NULL values. You must enter a value for each parameter, even if that value is NULL.

To signal the end of parameter definition, choose OK.


Supplying parameters at run time

Section topics 	See also

To create parameters at run time, you can use the

ParamByName method to assign values to parameters based on the parameters' names.

Params property to assign values to parameters based on the parameters' ordinal location.

Params.ParamValues property to assign values to a parameter based on the parameters' names. This method uses variants and avoids the need to cast values.

For example, the following code uses ParamByName to assign the text of an edit box to the Company field:

Query1.ParamByName('Company').AsString := Edit1.Text;

The same code can be rewritten using the Params property (it assumes that Company is the first parameter in the query):

Query1.Params[0].AsString := Edit1.Text;

The same code can be rewritten using the Params.ParamValues property. Note that because ParamValues is a variant, type conversions are handled automatically using this method:

Query1.Params.ParamValues['Company'] := Edit1.Text;

Assigning values to parameters based on parameter name

ParamByName is a function that enables an application to assign values to parameters based on the parameters' names. This is better and safer than using the Params property directly because you don't have to know the actual order of the parameters to which you assign values. Instead of providing the ordinal location of a parameter as you must when indexing directly into the array of strings in the Params property, you pass the parameter name as an argument to ParamByName.

For example, suppose a query component named CountryQuery has the following statement for its SQL property:

INSERT INTO COUNTRY (NAME, CAPITAL, POPULATION)

 VALUES (:name, :capital, :population)

The following statements assign parameter values to a set of specified parameters:

CountryQuery.ParamByName('name').AsString := 'Lichtenstein';

CountryQuery.ParamByName('capital').AsString := 'Vaduz';

CountryQuery.ParamByName('population').AsInteger := 420000;

Assigning values to parameters based on parameter order

When you create a parameterized query, Delphi creates an array of parameter objects for the query in the Params property. Params is a zero-based array of TParam objects with an element for each parameter in the query. The first parameter is Params[0], the second Params[1], and so on.

For example, given this query statement:

INSERT INTO COUNTRY (NAME, CAPITAL, POPULATION)

 VALUES (:name, :capital, :population)

then an application could specify values for each of these parameters as follows:

CountryQuery.Params.Items[0].AsString := 'Lichtenstein';

CountryQuery.Params.Items[1].AsString := 'Vaduz';

CountryQuery.Params.Items[2].AsInteger := 420000;

These statements bind the value "Lichtenstein" to the :name parameter, "Vaduz" to the :capital parameter, and 420000 to the :population parameter.


Using a data source to bind parameters

Section topics 	See also

If parameter values for a parameterized query are not bound at design time, Delphi attempts to supply values for them based on the query component's DataSource property. DataSource specifies a different table or query component that Delphi can search for field names that match the names of unbound parameters. This search dataset must be created and populated before you create the query component that uses it. If matches are found in the search dataset, Delphi binds the parameter values to the values of the fields in the current record pointed to by the data source.

You can create a simple application to understand how to use the DataSource property to link a query in a master-detail form. Suppose the data module for this application is called, LinkModule, and that it contains a query component called OrdersQuery that has the following SQL property:

SELECT CustNo, OrderNo, SaleDate

 FROM Orders

 WHERE CustNo = :CustNo

The LinkModule data module also contains:

A TTable dataset component named CustomersTable linked to the CUSTOMER.DB table.

A TDataSource component named OrdersSource. The DataSet property of OrdersSource points to OrdersQuery.

A TDataSource named CustomersSource linked to CustomersTable. The DataSource property of the OrdersQuery component is also set to CustomersSource. This is the setting that makes OrdersQuery a linked query.

Suppose, too, that this application has a form, named LinkedQuery that contains two data grids, a Customers Table grid linked to CustomersSource, and an Order Query grid linked to OrdersSource.

The following figure illustrates how this application appears at design time.

Sample master/detail query form and data module at design time

Note:	If you build this application, create a table component and its data source before creating the query component.

If you compile this application, at run time the :CustNo parameter in the SQL statement for OrdersQuery is not assigned a value, so Delphi tries to match the parameter by name against a column in table pointed to by CustomersSource. CustomersSource gets its data from CustomersTable, which, in turn, derives its data from the CUSTOMER.DB table. Because CUSTOMER.DB contains a column called "CustNo," the value from the CustNo field in the current record of the CustomersTable dataset is assigned to the :CustNo parameter for the OrdersQuery SQL statement. The grids are linked in a master-detail relationship. At run time, each time you select a different record in the Customers Table grid, the OrdersQuery SELECT statement executes to retrieve all orders based on the current customer number.


Executing a query

Section topics 	See also

After you specify an SQL statement in the SQL property, and after you set any parameters for the query, you can execute the query. When a query is executed, the BDE receives and processes SQL statements from your application.

Note:	Before you execute a query for the first time, you may want to call the Prepare method to improve query performance. Prepare initializes the BDE and the database server, each of which preallocates system resources for the query. For more information about preparing a query, see "Preparing a query."

The following sections describe executing both static and dynamic SQL statements at design time and at run time.


Executing a query at design time

Section topics 	See also

To execute a query at design time, set its Active property to True.

The results of the query, if any, are displayed in any data-aware controls associated with the query component.


Executing a query at runtime

Section topics 	See also

To execute a query at runtime, use one of the following methods:

Open executes a query that returns a result set, such as SELECT.

ExecSQL executes a query that does not return a result set, such as INSERT, UPDATE, or DELETE.

Note:	If you do not know at design time whether a query will return a result set at run time, code both types of query execution statements in a try...except block. Put a call to the Open method in the try clause, and put the ExecSQL method in the except clause. To avoid executing a statement twice (for example, in the case of an empty result set), check for an exception of type ENoResultSet before executing the ExecSQL method in the except clause.

Executing a query that returns a result set

To execute a query that returns a result set, such as a SELECT statement:

1.	Call Close to ensure that the query is not already open. If a query is already open you cannot open it again without first closing it. Closing a query and reopening it fetches a new version of data from the server.

2.	Call Open to execute the query.

For example:

CustomerQuery.Close;

CustomerQuery.Open; // Returns a result set

For information on navigating within a result set, see "Disabling bidirectional cursors." For information on editing and updating a result set, see "Working with result sets."

Executing a query without a result set

To execute a query that does not return a result set, such as INSERT, UPDATE, or DELETE, call Open to execute the query.

For example:

CustomerQuery.ExecSQL; // Does not return a result set


Preparing a query

Section topics 	See also

Preparing a query is an optional step that precedes query execution. Preparing a query submits the SQL statement and its parameters, if any, to the BDE for parsing, resource allocation, and optimization. The BDE, in turn, notifies the database server to prepare for the query. The server, too, may allocate resources for the query. These operations improve query performance, making your application faster, especially when working with updateable queries.

An application can prepare a query by calling the Prepare method. If you do not prepare a query before executing it, then Delphi automatically prepares it for you the first time you call Open or ExecSQL. Even though Delphi prepares queries for you, it is better programming practice to prepare a query explicitly. That way your code is self-documenting, and your intentions are clear. For example:

CustomerQuery.Close;

if not CustomerQuery.Prepared then

 CustomerQuery.Prepare;

CustomerQuery.Open;

This example checks the query component's Prepared property to determine if a query is already prepared. Prepared is a Boolean value that is True if a query is already prepared. If not, it calls the Prepare method before calling Open.


Unpreparing a query to release resources

Section topics 	See also

The UnPrepare method sets the Prepared property to False. UnPrepare

Ensures that the SQL property is reprepared prior to executing it again.

Notifies the BDE to release the internal resources allocated for the statement.

Notifies the server to release any resources it has allocated for the statement.

To unprepare a query:

CustomerQuery.UnPrepare;

Note:	When you change the text of the SQL property for a query, Delphi automatically closes and unprepares the query.


Creating heterogenous queries

Section topics 	See also

Delphi supports heterogeneous queries, queries made against tables in more than one database. A heterogeneous query may join tables on different servers, and even different types of servers. For example, a heterogeneous query might involve a table in a Oracle database, a table in a Sybase database, and a local dBASE table. When you execute a heterogeneous query, the BDE parses and processes the query using Local SQL, so extended, server-specific SQL syntax is not supported.

To perform a heterogeneous query:

1.	Define a BDE standard alias that references a local directory, and set the DatabaseName property of the query component to that alias. To define BDE aliases use the Database Explorer.

2.	Define separate BDE aliases for each database accessed in the query.

3.	Specify the SQL statement to execute in the SQL property. Precede each table name in the SQL statement with the BDE alias for the database where that table can be found.

4.	Set any parameters for the query in the Params property.

5.	Call Prepare to prepare the query for execution prior to executing it for the first time.

6.	Call Open or ExecSQL depending on the type of query to execute.

For example, suppose you define an alias called Oracle1 for an Oracle database that has a CUSTOMER table, and Sybase1 for a Sybase database that has an ORDERS table. A simple query against these two tables would be:

SELECT CUSTOMER.CUSTNO, ORDERS.ORDERNO

FROM ":Oracle1:CUSTOMER", ":Sybase1:ORDERS"

WHERE CUSTNO = 1503


Improving query performance

Section topics 	See also

Following are steps you can take to improve query execution speed:

Set a query's UniDirectional property to True if you do not need to navigate backward through a result set (SQL-92 does not, itself, permit backward navigation through a result set). By default, UniDirectional is False because the BDE supports bidirectional cursors by default.

Prepare the query before execution. This is especially helpful when you plan to execute a single query several times. You need only prepare the query once, before its first use. For more information about query preparation, see "Preparing a query."


Disabling bidirectional cursors

Section topics 	See also

The UniDirectional property determines whether or not BDE bidirectional cursors are enabled for a query in Delphi. When a query returns a result set, it also receives a cursor, or pointer to the first record in that result set. The record pointed to by the cursor is the currently active record. The current record is the one whose field values are displayed in data-aware components associated with the result set's data source.

UniDirectional is False by default, meaning that the cursor for a result set can navigate both forward and backward through its records. Bidirectional cursor support requires some additional processing overhead, and can slow some queries. To improve query performance, you may be able to set UniDirectional to True, restricting a cursor to forward movement through a result set.

If you do not need to be able to navigate backward through a result set, you can set UniDirectional to True for a query. Set UniDirectional before preparing and executing a query. The following code illustrates setting UniDirectional prior to preparing and executing a query:

if not CustomerQuery.Prepared then

begin

 CustomerQuery.UniDirectional := True;

 CustomerQuery.Prepare;

end;

CustomerQuery.Open; // Returns a result set with a one-way cursor


Working with result sets

Section topics 	See also

By default, the result set returned by a query is read-only. Your application can display field values from the result set in data-aware controls, but users cannot edit those values. To enable editing of a result set, your application must request a "live" result set.


Enabling editing of a result set

Section topics 	See also

To request a result set that users can edit in data-aware controls, set a query component's RequestLive property to True. Setting RequestLive to True does not guarantee a live result set, but the BDE attempts to honor the request whenever possible. There are some restrictions on live result set requests, depending on whether or not a query uses the local SQL parser or a server's SQL parser. Heterogeneous joins and queries executed against Paradox or dBASE are parsed by the BDE using local SQL. Queries against a remote database server are parsed by the server.

If an application requests and receives a live result set, Delphi sets the CanModify property for the query component to True.

If an application requests a live result set, but the SELECT statement syntax does not allow it, the BDE returns either

A read-only result set for queries made against Paradox or dBASE.

An error code for pass-through SQL queries made against a remote server.


Local SQL requirements for a live result set

Section topics 	See also

For queries that use the local SQL parser, the BDE offers expanded support for updatable, live result sets in both single table and multi-table queries. The local SQL parser is used when a query is made against one or more dBASE or Paradox tables, or one or more remote server tables when those table names in the query are preceded by a BDE database alias. The following sections describe the restrictions that must be met to return a live result set for local SQL.

Restrictions on live queries

A live result set for a query against a single table or view is returned if the query does not contain any of the following:

JOIN, UNION, INTERSECT or MINUS clauses.

A DISTINCT clause in the SELECT statement.

Aggregate functions.

Base tables or views that are not updatable.

GROUP BY or HAVING clauses.

Subqueries.

ORDER BY clauses not based on an index.

Field names that include SQL reserved words.

Restrictions on live joins

A live result set for a join is returned if:

Only two tables are involved in the join.

All joins are left-to-right outer joins.

All joins on Paradox and dBASE tables can be satisfied by existing indexes.

Output ordering is not defined.

Each table in the join is a base table.

All restrictions that apply to live queries are also met. For more information, see "Restrictions on live queries."


Remote server SQL requirements for a live result set

Section topics 	See also

For queries that use passthrough SQL, which includes all queries made solely against remote database servers, live result sets are restricted to the standards defined by SQL-92 and any additional, server-imposed restrictions.

SQL-92 restrictions that must be met in order to return a live result set are as follows. A query cannot contain:

A DISTINCT clause in the SELECT statement.

Aggregate functions

References to more than one base table or updatable views (i.e. joins).

GROUP BY or HAVING clauses.

Subqueries that reference the table in the FROM clause.

Correlated subqueries.


Restrictions on updating a live result set

Section topics 	See also

If a query returns a live result set, you may not be able to update the result set directly if the result set contains linked fields or you switch indexes before attempting an update. If these conditions exist, you may be able to treat the result set as a read-only result set, and update it accordingly.


Updating a read-only result set

Section topics 	See also

Applications can update data returned in a read-only result set if they are using cached updates.

To update a read-only result set associated with a query component:

1.	Add a TUpdateSQL component to the data module in your application to essentially give you the ability to post updates to a read-only dataset.

2.	Enter the SQL update statement for the result set to the update component's ModifySQL, InsertSQL, or DeleteSQL properties.

3.	Set the query component's CachedUpdate property to True.

For more information about using cached updates, see "Working with cached updates."


See also

TQuery

TUpdateSQL

Queries for server developers

Queries for desktop developers


 Working with stored procedures

Section topics 	See also

These topics describe how to use the TStoredProc dataset component in your database applications. A stored procedure component is a descendant of TDataSet, and inherits its fundamental behaviors from TDataSet.

A stored procedure component encapsulates a stored procedure on a remote database server such as Sybase, Microsoft SQL Server, Oracle, or InterBase. The stored procedure component enables Delphi applications to execute server stored procedures.

A stored procedure is a set of statements, stored as part of a remote server's database metadata (like tables, indexes, and domains). A stored procedure performs a frequently-repeated database-related task on the server and passes results to a client application, such as a Delphi database application.

A stored procedure is usually passed some parameters for processing. It usually returns output parameters in addition to a result dataset, and some procedures return a result parameter as well. Your application can display records from the dataset in data-aware controls, and it can also examine and process the output parameters. To learn about support for stored procedures on a server, see your server's documentation.

Note:	InterBase supports two types of stored procedures, one of which is called a select procedure because it is called within the context of a SELECT statement. To use an InterBase select procedure in Delphi, use a TQuery component instead of a TStoredProc component.


When should you use stored procedures?

Section topics 	See also

If your server defines stored procedures, you should use them if they apply to the needs of your application. A database server developer creates stored procedures to handle frequently-repeated database-related tasks. Often, operations that act upon large numbers of rows in database tables--or that use aggregate or mathematical functions--are candidates for stored procedures. If stored procedures exist on the remote database server your application uses, you should take advantage of them in your application. Chances are you need some of the functionality they provide, and you stand to improve the performance of your database application by:

Taking advantage of the server's usually greater processing power and speed.

Reducing the amount of network traffic since the processing takes place on the server where the data resides.

For example, consider an application that needs to compute a single value: the standard deviation of values over a large number of records. To perform this function in your Delphi application, all the values used in the computation must be fetched from the server, resulting in increased network traffic. Then your application must perform the computation. Because all you want in your application is the end result--a single value representing the standard deviation--it would be far more efficient for a stored procedure on the server to read the data stored there, perform the calculation, and pass your application the single value it requires.

See your server's database documentation for more information about its support for stored procedures.


Using a stored procedure

Section topics 	See also

To access a stored procedure on a server, an application must:

1.	Instantiate a stored procedure component and optionally associate it with a stored procedure on the server. For more information about creating a stored procedure, see "Creating a stored procedure component."

2.	Provide input parameter values to the stored procedure component, if necessary. When a stored procedure component is not associated with stored procedure on a server, you must provide additional input parameter information, such as parameter names and data types. For more information about providing input parameter information, see "Setting parameter information at design time."

3.	Execute the stored procedure.

4.	Process any result and output parameters. As with any other dataset component, you can also examine the result dataset returned from the server. For more information about result and output parameters, see "Accessing output and result parameters." For information about viewing records in a dataset, see "Accessing the result dataset."


Creating a stored procedure component

Section topics 	See also

To create a stored procedure component for a stored procedure on a database server:

1.	Place a stored procedure component from the Data Access page of the Component palette in a data module.

2.	Optionally set the DatabaseName property of the stored procedure component to the name of the database in which the stored procedure is defined. DatabaseName must be a BDE alias.

Normally you should specify the DatabaseName property, but if the server database against which your application runs is currently unavailable, you can still create and set up a stored procedure component by omitting the DatabaseName and supplying a stored procedure name and input, output, and result parameters at design time. For more information about input parameters, see "Using input parameters." For more information about output parameters, see "Using output parameters." For more information about the result parameter, see "Using the result parameter."

3.	Optionally set the StoredProcName property to the name of the stored procedure to use. If you provided a value for the DatabaseName property, then you can select a stored procedure name from the drop-down list for the property.

Normally you should specify the StoredProcName property if you specified the DatabaseName property.

4.	Double-click the Params property value box to invoke the StoredProc Parameters editor to examine input and output parameters for the stored procedure. If you did not specify a name for the stored procedure in Step 3, or you specified a name for the stored procedure that does not exist on the server specified in the DatabaseName property in Step 2, then when you invoke the parameters editor, it is empty.

Note:	You can also use the SQL Explorer to examine, edit, and create stored procedures on the server.

Not all servers return parameters or parameter information to Delphi. See your server's documentation to determine what information about its stored procedures it returns to client applications.

Note:	If you do not specify the DatabaseName property in Step 2, then you must use the StoredProc Parameters editor to set up parameters at design time. For information about setting parameters at run time, see "Setting parameter information at design time."


Understanding stored procedure parameters

Section topics 	See also

There are four types of parameters that can be associated with stored procedures:

Input parameters, used to pass values to a stored procedure for processing.

Output parameters, used by a stored procedure to pass return values to an application.

Input/output parameters, used to pass values to a stored procedure for processing, and used by the stored procedure to pass return values to the application.

A result parameter, used by some stored procedures to return an error or status value to an application. A stored procedure can return only one result parameter.

Whether a stored procedure uses a particular type of parameter depends both on the general language implementation of stored procedures on your database server and on a specific instance of a stored procedure. For example, individual stored procedures on any server may either be implemented using input parameters, or may not be. On the other hand, some uses of parameters are server-specific. For example, on MS-SQL Server and Sybase stored procedures always return a result parameter, but the InterBase implementation of a stored procedure never returns a result parameter.

Note:	Some stored procedures return a dataset in addition to output and result parameters. Applications can display dataset records in data-aware controls, but must separately process output and result parameters. For more information about displaying records in data-aware controls, see "Accessing the result dataset."


Using input parameters

Section topics 	See also

Most stored procedures require you to pass them one or more input parameters to use during processing. For example, a stored procedure that transfers funds between two bank accounts might require three input parameters: the account number from which to withdraw funds, the account number into which to deposit funds, and the amount of money to transfer.

If you connect to a remote database server by setting the DatabaseName and StoredProcName properties at design time, then you can use the StoredProc Parameters editor to view the names and data types of each input parameter, and you can set the values for the input parameters to pass to the server when you execute the stored procedure.

Important:	Do not change the names or data types for input parameters reported by the server, or when you execute the stored procedure an exception is raised.

Some servers--Informix, for example--do not report parameter names or data types. In these cases, use the SQL Explorer to look at the source code of the stored procedure on the server to determine input parameters and data types. See the SQL Explorer online help for more information.

At design time, if you do not receive a parameter list from a stored procedure on a remote server (for example because you are not connected to a server), then you must invoke the StoredProc Parameters editor, list each required input parameter, and assign each a data type and a value. For more information about using the StoredProc Parameters editor to create parameters, see "Setting parameter information at design time."


Using output parameters

Section topics 	See also

Most stored procedures return one or more output parameters. Output parameters may represent the sole return values for a stored procedure that does not also return a dataset, they may represent one set of values returned by a procedure that also returns a dataset, or they may represent values that have no direct correspondence to an individual record in the dataset returned by the stored procedure. Each server's implementation of stored procedures differs in this regard.

If you connect to a remote database server by setting the DatabaseName and StoredProcName properties at design time, then you can use the StoredProc Parameters editor to view the names and data types of each output parameter. Do not change the names or data types of output parameters reported by the server, or when you execute the stored procedure an exception is raised.

Note:	The source code for an Informix stored procedure may indicate that it returns output parameters even though you cannot not see output parameter information in the StoredProc Parameters editor. Informix translates output parameters into a single record dataset that you can view in your application's data-aware controls.

At design time, if you do not receive a parameter list from a stored procedure on a remote server (for example because you are not connected to a server), then you must invoke the StoredProc Parameters editor, list each required input parameter, and assign each a data type and a value.


Using input/output parameters

Section topics 	See also

An input/output parameter is used by your application to pass values to a stored procedure on the server, and is used by the server to return a different set of values to your application. See your database server documentation to determine if it supports or uses input/output parameters.

At design time, if you do not receive a parameter list from a stored procedure on a remote server (for example because you are not connected to a server), then you must invoke the StoredProc Parameters editor, list each required input parameter, and assign each a data type and a value.


Using the result parameter

Section topics 	See also

In addition to returning output parameters and a dataset, some stored procedures also return a single result parameter. The result parameter is usually used to indicate an error status or the number of records processed base on stored procedure execution. See your database server's documentation to determine if and how your server supports the result parameter.

At design time, if you do not receive a parameter list from a stored procedure on a remote server (for example because you are not connected to a server), then you must invoke the StoredProc Parameters editor, list each required input parameter, and assign each a data type and a value.


Viewing parameter information at design time

Section topics 	See also

If you have access to a database server from Delphi, then there are usually two ways to view information about the parameters used by a stored procedure:

Invoke the SQL Explorer to view the source code for a stored procedure on a remote server. The source code includes parameter declarations that identify the data types and names for each parameter.

Set the DatabaseName property of a stored procedure component to the BDE alias for your database server, set the StoredProcName property to the name of the stored procedure to view, and double-click the Params property value to invoke the StoredProc Parameters editor.

You can use the SQL Explorer to examine stored procedures on your database servers if you are using BDE native drivers (if you're using ODBC drivers you cannot examine stored procedures with the SQL Explorer). On the other hand, the amount of information returned about a stored procedure in the StoredProc Parameters editor depends on your database server. For example, an Informix server returns only the number of input parameters for a procedure, but does not return the names of those parameters or their data types.

To invoke the StoredProc Parameters editor:

1.	Select the stored procedure component.

2.	Choose Define Parameters from the component's context menu or double-click the Params property's value box in the Object Inspector.

The Parameter name list box displays all input, output, input/output and result parameters for the selected procedure. Not all stored procedures use all types of parameters. Information on parameters is retrieved from the server. For some servers some or all parameter information may not be accessible.

The Parameter type combo box describes whether a parameter selected in the list box is an input, output, input/output, or result parameter.

Note:	Sybase, MS-SQL, and Informix do not return parameter type information. Use the SQL Explorer to determine this information.

The Data type combo box lists the data type for a parameter selected in the list box.

Note:	Informix does not return data type information. Use the SQL Explorer to determine this information.

The Value edit box enables you to enter a value for a selected input parameter, and the Null Value check box enables you set a Null value for the selected input parameter if its data type supports Null values.

Note:	You can never set values for output and result parameters.


Setting parameter information at design time

Section topics 	See also

You can invoke the StoredProc Parameters editor at design time to set values for input parameters before executing a stored procedure. In addition, if you do not set a stored procedure's DatabaseName property, or you set the DatabaseName but do not also set StoredProcName to the name of a procedure listed by the server, then in addition to setting values for input parameters, you must also set parameter names and data types. If you must set parameter names and data types, then the Add button in the editor is enabled.

The Add button enables you to add parameters to a stored procedure definition you create.

To create a new parameter:

1.	Click Add.

2.	Specify a name for the procedure, and choose OK. The name you specified appears in the Parameter name list box.

3.	Select a parameter type (Input, Output, Input/Output, or Result) from the Parameter type combo box.

4.	Select a data type for the parameter from the Data type combo box. To return a result set from an Oracle stored procedure, set field type to Cursor.

5.	Specify a value to pass to the stored procedure in the Value edit box if the parameter type specified in Step 3 is Input or Input/Output. Optionally you can check the Null Value check box to pass a NULL value.

As soon as you add a new parameter to the Parameter name list box, the Delete and Clear buttons are enabled.

The Delete button enables you to remove parameters you have added. If you delete the only parameter you have added, the Clear and Delete buttons are disabled.

The Clear button removes all parameters from the list. If you clear all added parameters, Delete and Clear are disabled.

You can edit the definition for any parameter you add, but the parameters you add must match the parameters for the stored procedure on the server. To edit a parameter, select it in the Parameter name list box.

To signal the end of parameter definition, choose OK.


Setting input parameter values at run time

Section topics 	See also

To set input parameters at run time, access the Params property directly. For example, the following code assigns the text of an edit box to the first string in the array:

StoredProc1.Params[0].AsString:= Edit1.Text;

You can also access parameters by name using the ParamByName method:

StoredProc1.ParamByName('Company').AsString := Edit1.Text;

Params is an object of type TParams, that contains an array of TParam objects. Each TParam object has Add and Remove methods that you can also use to manipulate parameters. For more information, see TParams and TParam.


Preparing and executing a stored procedure

Section topics 	See also

To use a stored procedure, you can optionally prepare it, and then execute it.

You can prepare a stored procedure at:

Design time, by choosing OK in the Parameters editor.

Run time, by calling the Prepare method of the stored procedure component.

For example, the following code prepares a stored procedure for execution:

StoredProc1.Prepare;

Note:	You can prepare a stored procedure both at run time and at design time. In fact, if your application changes parameter information at run time, such as when using Oracle overloaded procedures, you should prepare the procedure again.

To execute a prepared stored procedure, call the ExecProc method for the stored procedure component. The following code illustrates code that prepares and executes a stored procedure:

StoredProc1.Params[0].AsString := Edit1.Text;

StoredProc1.Prepare;

StoredProc1.ExecProc;

Note:	If you attempt to execute a stored procedure before preparing it, Delphi automatically prepares it for you, and then unprepares it after it executes. If you plan to execute a stored procedure a number of times, it is more efficient to call Prepare yourself, and then call UnPrepare only once, when you no longer need to execute the procedure.

When you execute a stored procedure, it can return all or some of these items:

A dataset consisting of one or more records that can be viewed in data-aware controls associated with the stored procedure through a data source component.

Output parameters.

A result parameter that contains status information about the stored procedure's execution.

To determine the return items to expect from a stored procedure on your server, see your server's documentation.


Binding parameters

Section topics 	See also

When you prepare and execute a stored procedure, its input parameters are automatically bound to parameters on the server. On Sybase, you can bind blob types to parameters.

The ParamBindMode property enables you to specify how parameters in your stored procedure component should be bound to the parameters on the server.

By default ParamBindMode is set to pbmByName, meaning that parameters from the stored procedure component are matched to those on the server by name. Ordinarily this is the easiest method of binding parameters.

Some servers also support binding parameters by ordinal value. In this case the order in which you specify parameters in the Stored Proc Parameters editor is significant. The first parameter you specify is matched to the first input parameter on the server, the second parameter is matched to the second input parameter on the server, and so on. If your server supports parameter binding by ordinal value, you can set ParamBindMode to pbmByNumber.

Tip:	If you want to set ParamBindMode to pbmByNumber, you need to specify the correct parameter types in the correct order. You can view a server's stored procedure source code in the SQL Explorer to determine the correct order and type of parameters to specify.


Accessing output and result parameters

Section topics 	See also

In addition to a dataset, stored procedures on some database servers also return output parameters and a result parameter. To access a stored procedure's output parameters at run time, you can index into the Params string list, or you can use the ParamByName method to access the values. The following statement both set the value of a edit box based on output parameters:

Edit1.Text := StoredProc1.Params[6].AsString;

Edit1.Text : = StoredProc1.ParamByName('Contact').AsString;


Accessing the result dataset

Section topics 	See also

To display the result set from a stored procedure in data-aware controls:

1.	Place a datasource component on the data module.

2.	Set the DataSet property of the datasource to the name of the stored procedure component from which to receive data.

3.	Set the DataSource properties of the data-aware controls to the name of the datasource component.

The data-aware controls can now display the results from a stored procedure when the Active property for the stored procedure component is True.

Note:	Informix stored procedures do not return a dataset. Instead they return a singleton result in the output parameters. For more information about accessing output parameters, see "Accessing output and result parameters." MS-SQL Server and Sybase stored procedures can return multiple datasets, but TStoredProc accepts only the first dataset returned from the server.


Working with Oracle overloaded stored procedures

Section topics 	See also

Oracle servers allow overloading of stored procedures; overloaded procedures are different procedures with the same name. The stored procedure component's Overload property enables an application to specify the procedure to execute.

If Overload is zero (the default), there is assumed to be no overloading. If Overload is one (1), then Delphi executes the first stored procedure it finds on the Oracle server that has the overloaded name; if it is two (2), it executes the second, and so on.

Note:	Overloaded stored procedures may take different input and output parameters. See your Oracle server documentation for more information.


See also

TStoredProc

Understanding datasets


Working with cached updates

Section topics 	See also

Cached updates enable you to retrieve data from a database, cache and edit it locally, and then apply the cached updates to the database as a unit. When cached updates are enabled, updates to a dataset (such as posting changes or deleting records) are stored in an internal cache instead of being written directly to the dataset's underlying table. When changes are complete, your application calls a method that writes the cached changes to the database and clears the cache.

These topics describe when and how to use cached updates. It also describes the TUpdateSQL component that can be used in conjunction with cached updates to update virtually any dataset, particularly datasets that are not normally updatable.


Deciding when to use cached updates

Section topics 	See also

Cached updates are primarily intended to reduce data access contention on remote database servers by

Minimizing transaction times.

Minimizing network traffic.

While cached updates can minimize transaction times and drastically reduce network traffic, they may not be appropriate for all Delphi database client applications that work with remote servers. There are three areas of consideration when deciding to use cached updates:

Cached data is local to your application, and is not under transaction control. In a busy client/server environment this has two implications for your application:

Other applications can access and change the actual data on the server while your users edit their local copies of the data.

Other applications cannot see any data changes made by your application until it applies all its changes.

In master/detail relationships managing the order of applying cached updates can be tricky. This is particularly true when there are nested master/detail relationships where one detail table is the master table for yet another detail table and so on.

Applying cached updates to query-based datasets requires use of update objects.

Delphi provides cached update methods and transaction control methods you can use in your application code to handle these situations, but you must take care that you cover all possible scenarios your application is likely to encounter in your working environment.


Understanding the cached updates process

Section topics 	See also

This section provides a basic overview of how cached updates work in an application. If you have not used cached updates before, this process description serves as a guideline for implementing cached updates in your applications.

To use cached updates, the following order of processes must occur in an application:

1.	Enable cached updates. Enabling cached updates causes a read-only transaction that fetches as much data from the server as is necessary for display purposes and then terminates. Local copies of the data are stored in memory for display and editing. For more information about enabling and disabling cached updates, see "Enabling and disabling cached updates."

2.	Display and edit the local copies of records, permit insertion of new records, and support deletions of existing records. Both the original copy of each record and any edits to it are stored in memory. For more information about displaying and editing when cached updates are enabled, see "Applying cached updates."

3.	Fetch additional records as necessary. As a user scrolls through records, additional records are fetched as needed. Each fetch occurs within the context of another short duration, read-only transaction. (An application can optionally fetch all records at once instead of fetching many small batches of records.) For more information about fetching all records, see "Fetching records."

4.	Continue to display and edit local copies of records until all desired changes are complete.

5.	Apply the locally cached records to the database or cancel the updates. For each record written to the database, an OnUpdateRecord event is triggered. If an error occurs when writing an individual record to the database, an OnUpdateError event is triggered which enables the application to correct the error, if possible, and continue updating. When updates are complete, all successfully applied updates are cleared from the local cache. For more information about applying updates to the database, see "Applying cached updates."

If instead of applying updates, an application cancels updates, the locally cached copy of the records and all changes to them are freed without writing the changes to the database. For more information about canceling updates, see "Canceling pending cached updates."


Enabling and disabling cached updates

Section topics 	See also

Cached updates are enabled and disabled through the CachedUpdates properties of TTable, TQuery, and TStoredProc. CachedUpdates is False by default, meaning that cached updates are not enabled for a dataset.

To use cached updates, set CachedUpdates to True, either at design time (through the Object Inspector), or at run time. When you set CachedUpdates to True, the dataset's OnUpdateRecord event is triggered if you provide it. For more information about the OnUpdateRecord event, see "Creating an OnUpdateRecord event handler."

For example, the following code enables cached updates for a dataset at run time:

CustomersTable.CachedUpdates := True;

When you enable cached updates, a copy of all records necessary for display and editing purposes is cached in local memory. Users view and edit this local copy of data. Changes, insertions, and deletions are also cached in memory. They accumulate in memory until the current cache of local changes is applied to the database. If changed records are successfully applied to the database, the record of those changes are freed in the cache.

Note:	Applying cached updates does not disable further cached updates; it only writes the current set of changes to the database and clears them from memory.

To disable cached updates for a dataset, set CachedUpdates to False. If you disable cached updates when there are pending changes that you have not yet applied, those changes are discarded without notification. Your application can test the UpdatesPending property for this condition before disabling cached updates. For example, the following code prompts for confirmation before disabling cached updates for a dataset:

if (CustomersTable.UpdatesPending) then

 if MessageDlg('Discard pending updates?', mtConfirmation, mbYesNoCanel, 0) = mbOK then

   CustomersTable.CachedUpdates := False;


Fetching records

Section topics 	See also

By default, when you enable cached updates, Delphi automatically handles fetching of data from the database when necessary. Delphi fetches enough records for display. During the course of processing, many such record fetches may occur. If your application has specific needs, it can fetch all records at one time by calling the dataset's FetchAll method. FetchAll creates an in-memory, local copy of all records from the dataset. If a dataset contains many records or records with large BLOB fields, you may not want to use FetchAll.


Applying cached updates

Section topics 	See also

When a dataset is in cached update mode, changes to data are not actually written to the database until your application explicitly calls methods that apply those changes. Normally an application applies updates in response to user input, such as through a button or menu item.

Important:	To apply updates to a set of records retrieved by an SQL query that does not return a live result set, you must use a TUpdateSQL object to specify how to perform the updates. For updates to joins (queries involving two or more tables), you must provide one TUpdateSQL object for each table involved, and you must use the OnUpdateRecord event handler to invoke these objects to perform the updates. For more information about creating and using a TUpdateSQL object, see "Using update objects to update a dataset." For more information about creating and using an OnUpdateRecord event handler, see "Creating an OnUpdateRecord event handler."

Applying updates is a two-phase process that must take occurs in the context of a database component's transaction control to enable your application to recover gracefully from errors. For more information about transaction handling with database components, see "Connecting to databases."

When applying updates under database transaction control, the following events take place:

1.	A database transaction starts.

2.	Cached updates are written to the database (phase 1). If you provide it, an OnUpdateRecord event is triggered once for each record written to the database. If an error occurs when a record is applied to the database, the OnUpdateError event is triggered if you provide one. For more information using an OnUpdateRecord event, see "Creating an OnUpdateRecord event handler."

If the database write is unsuccessful:

Database changes are rolled back, ending the database transaction.

Cached updates are not committed, leaving them intact in the internal cache buffer.

If the database write is successful:

Database changes are committed, ending the database transaction.

Cached updates are committed, clearing the internal cache buffer (phase 2).

The two-phased approach to applying cached updates allows for effective error recovery, especially when updating multiple datasets (for example, the datasets associated with a master/detail form). For more information about handling update errors that occur when applying cached updates, see "Handling cached update errors."

There are actually two ways to apply updates. To apply updates for a specified set of datasets associated with a database component, call the database component's ApplyUpdates method. To apply updates for a single dataset, call the dataset's ApplyUpdates and Commit methods. These choices, and their strengths, are described in the following sections.


Applying cached updates with a database component method

Section topics 	See also

To apply cached updates to one or more datasets in the context of a database connection, call the database component's ApplyUpdates method. The following code applies updates to the CustomersQuery dataset in response to a button click event:

procedure ApplyButtonClick(Sender: TObject);

begin

 if not IsSQLBased and (TransIsolation <> tiDirtyRead) then {Paradox, dBASE, FoxPro? }

   TransIsolation := tiDirtyRead;             {then set TransIsolation to tiDirtyRead! }

 Database1.ApplyUpdates([CustomersQuery]);

end;

This method starts a transaction, and writes cached updates to the database. If successful, it also commits the transaction, and then commits the cached updates. If unsuccessful, this method rolls back the transaction, and does not change the status of the cached updates. In this latter case, your application should handle cached update errors through a dataset's OnUpdateError event. For more information about handling update errors, see "Handling cached update errors."

The main advantage to calling a database component's ApplyUpdates method is that you can update any number of dataset components that are associated with the database. The single argument to the ApplyUpdates method for a database is an array of dataset names. To apply updates for more than one dataset, separate dataset names with commas. For example, the following code applies updates for two tables used in a master/detail form:

 if not IsSQLBased and (TransIsolation <> tiDirtyRead) then {Paradox, dBASE, FoxPro? }

   TransIsolation := tiDirtyRead;             {then set TransIsolation to tiDirtyRead! }

Database1.ApplyUpdates([MasterTable, DetailTable]);

For more information about updating master/detail tables, see "Applying updates for master/detail tables."


Applying cached updates with dataset component methods

Section topics 	See also

You can apply updates for individual datasets directly using the dataset's ApplyUpdates and CommitUpdates methods. Each of these methods encapsulate one phase of the update process:

1.	ApplyUpdates writes cached changes to a database (phase 1)

2.	CommitUpdates clears the internal cache when the database write is successful (phase 2)

Applying updates at the dataset level gives you control over the order in which updates are applied to individual datasets. Order of update application is especially critical for handling master/detail relationships. To ensure the correct ordering of updates for master/detail tables, you should always apply updates at the dataset level. For more information see "Applying updates for master/detail tables."

If you call a dataset's cached update methods directly, you must explicitly apply the updates using a database transaction. For example, the following code illustrates how you must apply updates for the CustomerQuery dataset previously used to illustrate updates through a database method:

procedure ApplyButtonClick(Sender: TObject);

begin

 with CustomerQuery do

 begin

   Database1.StartTransaction;

   try

     if not IsSQLBased and (TransIsolation <> tiDirtyRead) then {Paradox, dBASE, FoxPro? }

       TransIsolation := tiDirtyRead;             {then set TransIsolation to tiDirtyRead!}

     ApplyUpdates; {try to write the updates to the database}

     Database1.Commit; {on success, commit the changes}

   except

     Database1.Rollback; (on failure, undo any changes}

     raise; {raise the exception again to prevent a call to CommitUpdates}

   end;

   CommitUpdates; {on success, clear the internal cache}

 end;

end;

If an exception is raised during the ApplyUpdates call, the database transaction is rolled back. Rolling back the transaction ensures that the underlying database table is not changed. The raise statement inside the except block re-raises the exception, thereby preventing the call to CommitUpdates. Because CommitUpdates is not called, the internal cache of updates is not cleared so that you can handle error conditions and possibly retry the update.


Applying updates for master/detail tables

Section topics 	See also

When you apply updates for master/detail tables, the order in which you list datasets to update is significant. Generally you should always update master tables before detail tables, except when handling deleted records. In complex master/detail relationships where the detail table for one relationship is the master table for another detail table, the same rule applies.

You can update master/detail tables at the database or dataset component levels. For purposes of control ( and of creating explicitly self-documented code), you may prefer to apply updates at the dataset level. The following example illustrates how you should code cached updates to two tables involved in a master/detail relationship:

Database1.StartTransaction;

try

 Master.ApplyUpdates;

 Detail.ApplyUpdates;

 Database1.Commit;

except

 Database1.Rollback;

 raise;

end;

Master.CommitUpdates;

Detail.CommitUpdates;

If an error occurs during the application of updates, this code also leaves both the cache and the underlying data in the database tables in the same state they were in before the calls to ApplyUpdates.

If an exception is raised during the call to Master.ApplyUpdates, it is handled like the single dataset case previously described. Suppose, however, that the call to Master.ApplyUpdates succeeds, and the subsequent call to Detail.ApplyUpdates fails. In this case, the changes are already applied to the master table. Because all data is updated inside a database transaction, however, even the changes to the master table are rolled back when Database1.Rollback is called in the except block. Furthermore, Master.CommitUpdates is not called because the exception which is re-raised causes that code to be skipped, so the cache is also left in the state it was before the attempt to update.

To appreciate the value of the two-phase update process, assume for a moment that ApplyUpdates is a single-phase process which updates the data and the cache. If this were the case, and if there were an error while applying the updates to the Detail table, then there would be no way to restore both the data and the cache to their original states. Even though the call to Database.Rollback would restore the database, there would be no way to restore the cache.

Note:	The tasks accomplished by both examples in this section could also have been accomplished by calling a database component's ApplyUpdates method instead of calling the datasets' ApplyUpdates and CommitUpdates methods.


Canceling pending cached updates

Section topics 	See also

Pending cached updates are updated records that are posted to the cache but not yet applied to the database. There are three ways to cancel pending cached updates:

To cancel all pending updates and disable further cached updates, set the CachedUpdates property to False.

To discard all pending updates without disabling further cached updates, call the CancelUpdates method

To cancel updates made to the current record call RevertRecord.

The following sections discuss these options in more detail.


Cancelling pending updates and disabling further cached updates

Section topics 	See also

To cancel further caching of updates and delete all pending cached updates without applying them, set the CachedUpdates property to False. When CachedUpdates is set to False, the CancelUpdates method is automatically invoked.


Canceling pending cached updates

Section topics 	See also

CancelUpdates clears the cache of all pending updates, and restores the dataset to the state it was in when the table was opened, cached updates were last enabled, or updates were last successfully applied. For example, the following statement cancels updates for the CustomersTable:

CustomersTable.CancelUpdates;

Note:	Calling CancelUpdates does not disable cached updating. It only cancels currently pending updates. To disable further cached updates, set the CachedUpdates property to False.


Canceling updates to the current record

Section topics 	See also

RevertRecord restores the current record in the dataset to the state it was in when the table was opened, cached updates were last enabled, or updates were last successfully applied. It is most frequently used in an OnUpdateError event handler to correct error situations. For example,

CustomersTable.RevertRecord;

If the record is not modified, this call has no effect. For more information about creating an OnUpdateError handler, see "Handling cached update errors."


Undeleting a cached record

Section topics 	See also

To undelete a cached record requires some coding because once the deleted record is posted to the cache, it is no longer the current record. In some instances, however, you may want to undelete such records. The process involves using the UpdateRecordTypes property to make the deleted records "visible," and then calling RevertRecord. Here is a code example that undeletes all deleted records in a table:

procedure UndeleteAll(DataSet: TDataSet);

begin

 with DataSet do

 begin

   UpdateRecordTypes := [rtDeleted];

   try

     First;

    while not EOF do

       RevertRecord;

   finally

     UpdateRecordTypes := [rtModified, rtInserted, rtUnmodified];

   end;

 end;

end;


Specifying visible records in the cache

Section topics 	See also

The UpdateRecordTypes property controls what type of records are visible in the cache when cached updates are enabled. UpdateRecordTypes works on cached records in much the same way as filters work on tables. UpdateRecordTypes is a set, so it can contain any combination of the following values:

Value	Meaning
rtModified	Modified records
rtInserted	Inserted records
rtDeleted	Deleted records
rtUnmodified	Unmodified records.

The default value for UpdateRecordTypes is [rtModified, rtInserted, rtUnmodified].

UpdateRecordTypes property is primarily useful in an OnUpdateError event handler for accessing deleted records so they can be undeleted through a call to RevertRecord. This property is also useful if you wanted to provide a way in your application for users to view only a subset of cached records, for example, all newly inserted (rtInserted) records. For more information about creating an OnUpdateError handler, see "Handling cached update errors."


Checking update status

Section topics 	See also

When cached updates are enabled for your application, you can keep track of each pending update record in the cache by examining the UpdateStatus property for the record. Checking update status is most frequently used in OnUpdateRecord and OnUpdateError event handlers. For more information about creating and using an OnUpdateRecord event, see "Creating an OnUpdateRecord event handler." For more information about creating and using an OnUpdateError event, see "Handling cached update errors."

As you iterate through a set of pending changes, UpdateStatus changes to reflect the update status of the current record. UpdateStatus returns one of the following values for the current record:

Value	Meaning
usUnmodified	Record is unchanged.
usModified	Record is changed.
usInserted	Record is a new record.
unDeleted	Record is deleted.

When a dataset is first opened all records will have an update status of usUnmodified. As records are inserted, deleted, and so on, the status values change. Here is an example of UpdateStatus property used in an OnCalcFields event handler to display an asterisk in a calculated field if its associated record is modified:

procedure TForm.CalcFields(DataSet: TDataSet);

begin

 if DataSet.UpdateStatus <> usUnmodified then

   Table1ModifiedRow.Value := '*'

 else

   Table1ModifiedRow.Value := Null;

end;

Note:	If a record's UpdateStatus is usModified, you can examine the OldValue property for each field in the dataset to determine its previous value. OldValue is meaningless for records with UpdateStatus values other than usModified. For more information about examining and using OldValue, see "Accessing a field's OldValue and NewValue properties."


Determining if you need to control the updating process

Section topics 	See also

Some of the time when you use cached updates, all you need to do is call ApplyUpdates to write cached changes to the database (for example, when you have exclusive access to a local Paradox or dBASE table through a TTable component). In most other cases, however, you either might want to or must provide additional processing to ensure that updates can be applied.

For example, you might want to use the OnUpdateRecord event to provide validation routines that adjust data before it is applied to the table, or you might want to use the OnUpdateRecord event to provide additional processing for records in master and detail tables before writing them to the database.

In many cases you must provide additional processing. For example, any time you enable cached updates and access data using a TQuery component that does not return a live set, you need to provide a TUpdateSQL object that provides the SQL statement or statement necessary to perform record updates. Furthermore, if you access multiple tables using a joined query, then you must provide one TUpdateSQL object for each table in the query, and you must use the OnUpdateRecord event to make sure each update object is used to write changes to the database.

The following sections describe how to create and use an TUpdateSQL object and how to create and use an OnUpdateRecord event.


Using update objects to update a dataset

Section topics 	See also

TUpdateSQL is an update component that uses SQL statements to update a dataset. You must provide one TUpdateSQL component for each underlying table accessed by the original query that you want to update.

Note:	If you use more than one update component to perform an update operation, you must create an OnUpdateRecord event to access each update component as appropriate.

An update component actually encapsulates three TQuery components. Each of these query components perform a single update task. One query component provides an SQL UPDATE statement for modifying existing records; a second query component provides an INSERT statement to add new records to a table; and a third component provides a DELETE statement to remove records from a table.

When you place an update component in a data module, you do not see the query components it encapsulates. They are created by the update component at run time based on three update properties you supply at design time:

ModifySQL specifies the UPDATE statement.

InsertSQL specifies the INSERT statement.

DeleteSQL specifies the DELETE statement.

At run time, when the update component is used to apply updates, the update component:

1.	Selects an SQL statement to execute based on the UpdateKind property for the dataset to which the update component belongs. UpdateKind specifies whether the current record is modified, inserted, or deleted.

2.	Provides parameter values to the SQL statement.

3.	Prepares and executes the SQL statement to perform the specified update.


Specifying the UpdateObject property for a dataset

Section topics 	See also

A dataset component's UpdateObject property is an optional reference to a component of type TUpdateObject. When ApplyUpdates is called on a read-only dataset, and UpdateObject is set, the update object component is invoked for each record that requires updating. For more information about working with TUpdateObject components, see "Creating SQL statements for update components."

Although there is only one UpdateObject property for a dataset component, there is no restriction on the number of update object components that can be used to perform the updates. In cases where more than one update object component is needed, use the OnUpdateRecord event handler for the dataset component to invoke the additional update object components. For more information about OnUpdateRecord, see "Creating an OnUpdateRecord event handler."


Creating SQL statements for update components

Section topics 	See also

To create the SQL statements for an update component:

1.	Place a TUpdateSQL component (found on the Data Access page in the Component palette) on a data module or form.

2.	Assign the update component to a dataset in the dataset's UpdateObject property. This step ensures that the Update SQL editor you invoke in the next step can determine suitable default values to use for SQL generation options.

3.	Double-click the update component to invoke the Update SQL editor. The editor creates SQL statements for the update component's ModifySQL, InsertSQL, and DeleteSQL properties based on the underlying data set and on the values you supply to it.

The Update SQL editor has two pages. The Options page is visible when you first invoke the editor. Use the Table Name combo box to select the table to update. When you specify a table name, the Key Fields and Update Fields list boxes are populated with available columns.

The Update Fields list box indicates which columns should be updated. When you first specify a table, all columns in the Update Fields list box are selected for inclusion. You can multi-select fields as desired.

The Key Fields list box is used to specify the columns to use as keys during the update. For Paradox, dBASE, and FoxPro the columns you specify here must correspond to an existing index, but this is not a requirement for remote SQL databases. Instead of setting Key Fields you can select the Primary Keys to choose a dataset's primary index.

Check the Quote Field Names check box if your server requires quotation marks around field names.

After you specify a table, select key columns, and select update columns, click Generate SQL to generate the preliminary SQL statements to associate with the update component's ModifySQL, InsertSQL, and DeleteSQL properties. In most cases you may want or need to fine tune the SQL statements Delphi generates.

To view and modify the generated SQL statements, select the SQL page . If you have generated SQL statements, then when you select this page, the statement for the ModifySQL property is already displayed in the SQL Text memo box. You can edit the statement in the box as desired.

Important:	Keep in mind that generated SQL statements are starting points for creating update statements. You may need to modify these statements to make them execute correctly. Test each of the statements directly yourself before accepting them.

Use the Statement Type radio buttons to switch among generated SQL statements and edit them as desired.

To accept the statements and associate them with the update component's SQL properties, click OK.

Handling updates to existing records

To update an existing record, use an update object's ModifySQL property to specify the SQL UPDATE statement that performs the update operation. In addition, you should also make sure you set the UpdateMode property for the parent dataset of the update object to its appropriate value. UpdateMode specifies how Delphi should find records to update. The following table lists the possible values for UpdateMode:

Value	Meaning
upWhereAll	(Default.) Update a record only if every field in the record exactly matches the values specified in the WHERE clause of the UPDATE statement. This is the most restrictive update mode.
upWhereKeyOnly	Update a record based on specified key columns. This is the least restrictive mode and should be used only if an application has exclusive access to the data.
upWhereChange	Update a record based on key columns and columns that are modified.
Understanding parameter substitution in generated SQL statements

Generated update SQL statements use a special form of parameter substitution that enables you to determine whether old or new field values are substituted on record update. When the Update SQL editor generates its statements, it determines which field values to use.

When the parameter name matches a column name in the table, the new value will be used. When the parameter name matches a column name prefixed by the string "OLD_", then the old value will be used.

New field values are typically used in the InsertSQL and ModifySQL statements. In the case of a deleted record, there are no new values, so the DeleteSQL property uses the ":OLD_FieldName" syntax. Old field values are also normally used in the WHERE clause of the SQL statement to determine which record to update.

For more information about old and new value parameter substitution, see "Accessing a field's OldValue and NewValue properties."


Using an update component's DataSet property

Section topics 	See also

An update component's DataSet property identifies the dataset to update. Normally this property is set automatically based on the name of the dataset that uses the update component. When you use more than one update component to update a dataset, however, you must supply this value at run time.


Accessing an update component's Query property

Section topics 	See also

The Query property of an update component provides access to the TQuery components called by the update component to perform updates. In most cases you do not need to access this property; but if you do, you can use UpdateKind parameter values as an index into the array of query components.

Note:	Query is not a property of the UpdateObject. When you reference the property from a dataset, you must typecast the UpdateObject property. For example:

(DataSet.UpdateObject as TUpdateSQL).Query[UpdateKind];

For an example of using this property, see"Calling the SetParams method."


Using an update component's SQL property

Section topics 	See also

At run time, an update component's SQL property provides an indexed method of accessing its ModifySQL, InsertSQL, and DeleteSQL properties. Use the values of UpdateKind as an index. For example:

SQL[ukModify] = ModifySQL

Normally, the properties indexed by the SQL property are set at design time using the Update SQL editor. You might, however, need to access these values at run time if you are generating a unique update SQL statement for each record and not using parameter binding. The following example generates a unique SQL property value for each row updated:

procedure TForm1.EmpAuditUpdateRecord(DataSet: TDataSet;

 UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);

begin

 with DataSet.UpdateObject do

 begin

   case UpdateKind of

     ukModified:

     begin

       SQL[UpdateKind].Text := Format('update emptab set Salary = %d where EmpNo = %d',

         [EmpAuditSalary.NewValue, EmpAuditEmpNo.OldValue]);

       ExecSQL(UpdateKind);

     end;

     ukInserted:

       {...}

     ukDeleted:

       {...}

   end;

 end;

 UpdateAction := uaApplied;

end;

Note:	Rely on the parameter substitutions supplied by update components before building your own SQL statements on the fly.


Calling the Apply method

Section topics 	See also

The Apply method for an update component applies updates for the current record. There are two steps involved in this process:

1.	Values for the record are bound to the parameters in the appropriate SQL statement.

2.	The SQL statement is executed.

This method is either automatically invoked by the associated dataset, or explicitly called from the OnUpdateRecord event. The UpdateKind parameter is used to determine which update SQL statement to use. If invoked by the associated dataset, the UpdateKind is set automatically. If you invoke the method in an OnUpdateRecord event, pass the UpdateKind parameter of the event to Apply.

If an exception is raised during the execution of the update program, execution continues in the OnUpdateError event, if it is defined.

Note:	The operations performed by Apply correspond to the SetParams and ExecSQL methods described in the following sections.


Calling the SetParams method

Section topics 	See also

The SetParams method for an update component uses special parameter substitution rules to substitute old and new field values into the update SQL statement. Ordinarily, SetParams is called automatically by the update component's Apply method. If you call Apply directly in an OnUpdateRecord event, do not call SetParams yourself.

SetParams determines for which SQL statement to set parameters by examining the UpdateKind parameter.

You need only call SetParams directly if you need special control of parameter binding. The following example illustrates one such use:

procedure TForm1.EmpAuditUpdateRecord(DataSet: TDataSet;

 UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);

begin

 with DataSet.UpdateObject as TUpdateSQL do

 begin

   SetParams(UpdateKind);

   if UpdateKind = ukModified then

     Query[UpdateKind].ParamByName('DateChanged').Value := Now;

   ExecSQL(UpdateKind);

 end;

 UpdateAction := uaApplied;

end;

Note:	This example assumes that the ModifySQL property for the update component is as follows:

UPDATE EMPAUDIT

SET EmpNo = :EmpNo, Salary = :Salary, Changed = :DateChanged

WHERE EmpNo = :OLD_EmpNo

In this example, the call to SetParams supplies values to the EmpNo and Salary parameters. The DateChanged parameter is not set because the name does not match the name of a field in the dataset, so the next line of code sets this value explicitly.


ExecSQL

Section topics 	See also

An update component's ExecSQL method executes a prepared update SQL statement. Ordinarily, ExecSQL is called automatically by the update component's Apply method. If you call Apply directly in an OnUpdateRecord event, do not call ExecSQL yourself.

ExecSQL determines which SQL statement to execute based on the records's UpdateKind parameter.

If an exception is raised during the execution of the update SQL statement, the OnUpdateError event is triggered.


Executing UpdateSQL statements

Section topics 	See also

When the ApplyUpdates method for a database or dataset component is called, update statements associated with an update component execute automatically if the update component is associated with a dataset through the dataset's UpdateObject property.

Update statements are not executed automatically if you use more than one update component to perform updates on a dataset. In this case (or when you want to carry out additional processing in ApplyUpdates), you must write an OnUpdateRecord event handler to execute the update statements associated with each update component used .


Updating a read-only result set

Section topics 	See also

Although the BDE attempts to provide an updatable, or "live" query result when the RequestLive property for a dataset component is True, there are some situations where it cannot do so. In these situations, you can manually update a dataset as follows:

1.	Add a TUpdateSQL component to the data module in your application.

2.	Set the dataset component's UpdateObject property to the name of the TUpdateSQL component in the data module.

3.	Enter the SQL update statement for the result set to the update component's ModifySQL, InsertSQL, or DeleteSQL properties, or use the Update SQL editor.

4.	Close the dataset.

5.	Set the dataset component's CachedUpdates property to True.

6.	Reopen the dataset.

Note:	In many circumstances, you may also want to write an OnUpdateRecord event handler for the dataset.


Creating an OnUpdateRecord event handler

Section topics 	See also

The OnUpdateRecord event handles cases where a single update component cannot be used to perform the required updates, or when your application needs more control over special parameter substitution.

DataSet.UpdateObject.Apply(UpdateKind);

To create an OnUpdateRecord event handler for a dataset:

1.	Select the dataset component.

2.	Choose the Events page in the Object Inspector.

3.	Double-click the OnUpdateRecord property value to invoke the code editor.

Here is the skeleton code for an OnUpdateRecord event handler:

procedure TForm1.DataSetUpdateRecord(DataSet: TDataSet;

 UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);

begin

 { Perform updates here... }

end;

The DataSet parameter specifies the dataset to update.

The UpdateKind parameter indicates the type of update to perform. When using an update component, you need to pass this parameter to its methods and properties that you access. You may also need to inspect this parameter if your handler performs any special processing based on the kind of update to perform.

The UpdateAction parameter indicates if you applied an update or not. The default value is uaFail. Unless you encounter a problem during updating, your event handler should set this value to uaApplied before exiting. If you decide not to update a particular record, set the value to uaSkip to preserve unapplied changes in the cache.

If you do not change the value for UpdateAction, the entire update operation for the dataset is aborted. For more information about UpdateAction, see "Specifying the action to take."

In addition to these parameters, you will typically want to make use of the OldValue and NewValue properties for the field component associated with the current record. For more information about OldValue and NewValue, see "Accessing a field's OldValue and NewValue properties."

Important:	The OnUpdateRecord event, like the OnUpdateError and OnCalcFields event handlers, should never call any methods that change which record in a dataset is the current record.

OnUpdateRecord generally uses one or more update components to update a dataset. Using update components is the easiest way to update a dataset, but it is not a requirement. For example, the following code uses a table component to perform updates:

procedure TForm1.EmpAuditUpdateRecord(DataSet: TDataSet;

 UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);

begin

 if UpdateKind = ukInsert then

   UpdateTable.AppendRecord([DataSet.Fields[0].NewValue, DataSet.Fields[1].NewValue])

 else

   if UpdateTable.Locate('KeyField', DataSet.Fields[0].OldValue, []) then

     case UpdateKind of

     ukModify:

       begin

         Edit;

         UpdateTable.Fields[1].Value := DataSet.Fields[1].Value;

         Post;

       end;

     ukModify: DeleteRecord;

     end;

   UpdateAction := uaApplied;

end;

More typically, however, an OnUpdateRecord event handler uses two or more update components:

procedure TForm1.EmpAuditUpdateRecord(DataSet: TDataSet;

 UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);

begin

 EmployeeUpdateSQL.Apply(UpdateKind);

 JobUpdateSQL.Appply(UpdateKind);

 UpdateAction := uaApplied;

end;

In this example the DataSet parameter is not used. This is because the update components called in the handler belong to a data module, and are not referenced through the UpdateObject property.

Important:	If you both specify an update component in the UpdateObject property for a dataset, and define an OnUpdateRecord event handler for it, the update component pointed to in the UpdateObject property is not invoked unless you explicitly call its Apply method in your handler.


Handling cached update errors

Section topics 	See also

Because there is a delay between the time a record is first cached and the time cached updates are applied, there is a possibility that another application may change the record in a database before your application applies its updates. Fortunately, the Borland Database Engine (BDE) specifically checks for this situation when attempting to apply updates, and reports it as an error.

A dataset component's OnUpdateError event enables you to catch and respond to this error (and any other update error as well). You should code this event handler if you use cached updates. If you do not, and an error occurs, the entire update operation fails.

Caution:	Do not call any dataset methods that change the current record (such as Next and Prior) in an OnUpdateError event handler. Doing so causes the event handler to enter an endless loop.

Here is the skeleton code for an OnUpdateError event handler:

procedure TForm1.DataSetUpdateRecord(DataSet: TDataSet;

 UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);

begin

 { Perform updates here... }

end;

The following sections describe each of the parameters passed to the update error handler, and how those parameters are used.


Referencing the dataset to which to apply updates

Section topics 	See also

DataSet references the dataset to which updates are applied. To process new and old record values during error handling you must supply this reference.


Indicating the type of update that generated an error

Section topics 	See also

UpdateKind is a parameter of type TUpdateKind. It describes the type of update that generated the error. Unless your error handler takes special actions based on the type of update being carried out, your code probably will not make use of this parameter.

The following table lists possible values for UpdateKind:

Value	Meaning
ukModify	Editing an existing record caused an error.
ukInsert	Inserting a new record caused an error.
ukDelete	Deleting an existing record caused an error.


Specifying the action to take

Section topics 	See also

UpdateAction is a parameter of type TUpdateAction. When your update error handler is first called, the value for this parameter is always set to uaFail. Based on the error condition for the record that caused the error and what you do to correct it, you typically set UpdateAction to a different value before exiting the handler. UpdateAction can be set to one of the following values:

Value	Meaning
uaAbort	Aborts the update operation without displaying an error message.
uaFail	Aborts the update operation, and displays an error message. This is the default value for UpdateAction when you enter an update error handler.
uaSkip	Skips updating the row, but leaves the update for the record in the cache
uaRetry	Repeats the update operation. Correct the error condition before setting UpdateAction to this value.
uaApplied	Not used in error handling routines.

If your error handler can correct the error condition that caused the handler to be invoked, set UpdateAction to the appropriate action to take on exit. For error conditions you correct, set UpdateAction to uaRetry to apply the update for the record again.

When set to uaSkip, the update for the row that caused the error is skipped, and the update for the record remains in the cache after all other updates are completed.

uaFail and uaAbort are alike. Both cause the entire update operation to end. uaFail raises an exception, and displays an error message. uaAbort raises a silent exception (does not display an error message).

Note:	When an error occurs during application of updates, unless ApplyUpdates methods are called from within a try...except statement, an error message is displayed after the call to ApplyUpdates is made. If you also display an error message to the user from inside your error event handler, your application may display the same error message twice. To prevent error message duplication, set UpdateAction to uaAbort to turn off error message display.

The uaApplied value should only be used inside an OnUpdateRecord event. Do not set this value in an update error handler. For more information about update record events, see "Creating an OnUpdateRecord event handler."


Working with error message text

Section topics 	See also

The E parameter is usually of type EDBEngineError. From this exception type, you can extract an error message that you can display to users in your error handler. For example, the following code could be used to display the error message in the caption of a dialog box:

ErrorLabel.Caption := E.Message;

This parameter is also useful for determining the actual cause of the update error. You can extract specific error codes from EDBEngineError, and take appropriate action based on it. For example, the following code checks to see if the update error is related to a key violation, and if it is, it sets the UpdateAction parameter to uaSkip:

{ Add 'Bde' to your uses clause for this example}

if E is EDBEngineError then

 with EDBEngineError(E) do

 begin

   if Errors[ErrorCount - 1].ErrorCode = DBIERR_KEYVIOL then

     UpdateAction := uaSkip  { Key violation, just skip this record }

   else

     UpdateAction := uaAbort { Don't know what's wrong, abort the update }

 end;


Accessing a field's OldValue and NewValue properties

Section topics 	See also

When cached updates are enabled for records, the original values for fields in each record are stored in a read-only TField property called OldValue. Changed values are stored in the analogous TField property NewValue. These values provide the only way to inspect and change update values in OnUpdateError and OnUpdateRecord event handlers. For more information about OnUpdateRecord, see "Creating an OnUpdateRecord event handler."

In some cases, you may be able to use these properties to determine the cause of an error and correct it. For example, the following code handles corrections to a salary field which can be increased only by 25 percent at a time (enforced by a constraint on the server):

var

 SalaryDif: Integer;

 OldSalary: Integer;

begin

 OldSalary := EmpTabSalary.OldValue;

 SalaryDif := EmpTabSalary.NewValue - OldSalary;

 if SalaryDif / OldSalary > 0.25 then

 begin

   { Increase was too large, drop it back to 25% }

   EmpTabSalary.NewValue := OldSalary + OldSalary * 0.25;

   UpdateAction := uaRetry;

 end

 else

   UpdateAction := uaSkip;

end;

NewValue is decreased to 25 percent in the case where the salary increased by a larger percentage, and then the update operation is retried. To improve the efficiency of this routine, the OldValue parameter is stored in a local variable.


See also

TStoredProc

TQuery

TTable

TDataSet

TDatabase

TUpdateSQL

Connecting to databases


Using data controls

Section topics 	See also

These topics describe how to use data-aware visual controls to display and edit data associated with the tables and queries in your database application. A data-aware control derives display data from a database source outside the application, and can also optionally post (or return) data changes to a data source.

These topics describe basic features common to all data control components, then describe how and when to use individual components. In particular, they describe the following features common to data controls:

Associating a data control with a data set

Editing and updating data

Disabling and enabling data display

Refreshing data display

Most individual data controls are also described, including the navigator control, TDBNavigator, which enables you to provide a visual record navigation and editing tool to your users.

Several specialized and more complex data-aware controls are discussed in subsequent topics. Grid controls, TDBGrid, and TDBCtrlGrid, are discussed in "Working with grids." Decision support grid and graph controls are discussed in "Using decision support components."


Using common data control features

Section topics 	See also

You place data controls from the Data Controls page of the Component palette onto the forms in your database application. Data controls generally enable you to display and edit fields of data associated with the current record in a dataset. The following table summarizes the data controls that appear on the Data Controls page of the Component palette.

Data control	Description
TDBGrid	Displays information from a data source in a tabular format. Columns in the grid correspond to columns in the underlying table or query's dataset. Rows in the grid correspond to records.
TDBNavigator	Navigates through data records in a dataset. updating records, posting records, deleting records, canceling edits to records, and refreshing data display.
TDBText	Displays data from a field as a label.
TDBEdit	Displays data from a field in an edit box.
TDBMemo	Displays data from a memo or BLOB field in a scrollable, multi-line edit box.
TDBRichEdit	Displays formatted data from a field in an edit box.
TDBImage	Displays graphics from a data field in a graphics box.
TDBListBox	Displays a list of items from which to update a field in the current data record.
TDBComboBox	Displays a list of items from which to update a field, and also permits direct text entry like a standard data-aware edit box.
TDBCheckBox	Displays a check box that indicates the value of a Boolean field.
TDBRadioGroup	Displays a set of mutually exclusive options for a field.
TDBLookupListBox	Displays a list of items looked up from another dataset based on the value of a field.
TDBLookupComboBox	Displays a list of items looked up from another dataset based on the value of a field, and also permits direct text entry like a standard data-aware edit box.
TDBCtrlGrid	Displays a configurable, repeating set of data-aware controls within a grid.

Data controls are data-aware at design time. When you set a control's DataSource property to an active data source while building an application, you can immediately see live data in the controls. You can use the Fields editor to scroll through a dataset at design time to verify that your application displays data correctly without having to compile and run the application. For more information about the Fields editor, see "Creating persistent fields."

At run time, data controls display data and, if your application, the control, and the database all permit it, a user can edit data through the control.


Displaying and editing data in a data-aware control

Section topics 	See also

To display data in a data control:

1.	Place a dataset and a data source in a data module (or on a form), and set their properties as appropriate.

2.	Place a data control from the Data Access page of the Component palette onto a form.

3.	Set the DataSource property of the control to the name of a data source component from which to get data. A data source component acts as a conduit between the control and a dataset containing data.

4.	Set the DataField property of the control to the name of a field to display, or select a field name from the drop-down list for the property. This step does not apply to TDBGrid, TDBCtrlGrid, and TDBNavigator because they access all available fields within a dataset.

5.	Set the Active property of the dataset to True to display data in the control.


Enabling mouse, keyboard, and timer events

Section topics 	See also

The Enabled property of a data control determines whether it responds to mouse, keyboard, or timer events, and passes information to its data source. The default setting for this property is True.

To prevent mouse, keyboard, or timer events from accessing a data control, set its Enabled property to False. When Enabled is False, a data source does not receive information from the data control. The data control continues to display data, but the text displayed in the control is dimmed.


Enabling editing in controls on user entry

Section topics 	See also

A dataset must be in dsEdit state to permit editing to its data. The AutoEdit property of the data source to which a control is attached determines if the underlying dataset enters dsEdit mode when data in a control is modified in response to keyboard or mouse events. When AutoEdit is True (the default), dsEdit mode is set as soon as editing commences. If AutoEdit is False, you must provide a TDBNavigator control with an Edit button (or some other method) to permit users to set dsEdit state at run time. For more information about TDBNavigator, see "Navigating and manipulating records."


Editing data in a control

Section topics 	See also

The ReadOnly property of a data control determines if a user can edit the data displayed by the control. If False (the default), users can edit data. To prevent users from editing data in a control, set ReadOnly to True.

Properties of the data source and dataset underlying a control also determine if the user can successfully edit data with a control and can post changes to the dataset.

The Enabled property of a data source determines if controls attached to a data source are able to display fields values from the dataset, and therefore also determine if a user can edit and post values. If Enabled is True (the default), controls can display field values.

The ReadOnly property of the dataset determines if user edits can be posted to the dataset. If False (the default), changes are posted to the dataset. If True, the dataset is read-only.

Note:	An additional read-only, run-time property CanModify that determines if a dataset can be modified. CanModify is set to True if a database permits write access. If CanModify is False, a dataset is read-only. Query components that perform inserts and updates are, by definition, able to write to an underlying database, provided that your application and user have sufficient write privileges to the database itself.

The following table summarizes the factors that determine if a user can edit data in a control and post changes to the database.

Data control ReadOnly property	Data source Enabled property	Dataset ReadOnly property	Dataset CanModify property	Database write access	Can write to database?
False	True	False	True	Read/Write	Yes
False	True	False	False	Read-only	No
False	False	--	--	--	No
True	--	--	--	--	No

In all data controls except TDBGrid, when you modify a field, the modification is copied to the underlying field component in a dataset when you Tab from the control. If you press Esc before you Tab from a field, Delphi abandons the modifications, and the value of the field reverts to the value it held before any modifications were made.

In TDBGrid, modifications are copied only when you move to a different record; you can press Esc in any record of a field before moving to another record to cancel all changes to the record.

When a record is posted, Delphi checks all data-aware components associated with the dataset for a change in status. If there is a problem updating any fields that contain modified data, Delphi raises an exception, and no modifications are made to the record.


Disabling and enabling data display

Section topics 	See also

When your application iterates through a dataset or performs a search, you should temporarily prevent refreshing of the values displayed in data-aware controls each time the current record changes. Preventing refreshing of values speeds the iteration or search and prevents annoying screen-flicker.

DisableControls is a dataset method that disables display for all data-aware controls linked to a dataset. As soon as the iteration or search is over, your application should immediately call the dataset's EnableControls method to re-enable display for the controls.

Usually you disable controls before entering an iterative process. The iterative process itself should take place inside a try...finally statement so that you can re-enable controls even if an exception occurs during processing. The finally clause should call EnableControls. The following code illustrates how you might use DisableControls and EnableControls in this manner:

CustTable.DisableControls;

try

 CustTable.First; { Go to first record, which sets EOF False }

 while not CustTable.EOF do { Cycle until EOF is True }

 begin

   { Process each record here }

   ...

   CustTable.Next; { EOF False on success; EOF True when Next fails on last record }

 end;

finally

 CustTable.EnableControls;

end;


Refreshing data

Section topics 	See also

The Refresh method for a dataset flushes local buffers and refetches data for an open dataset. You can use this method to update the display in data-aware controls if you think that the underlying data has changed because other applications have simultaneous access to the data used in your application.

Important:	Refreshing can sometimes lead to unexpected results. For example, if a user is viewing a record deleted by another application, then the record disappears the moment your application calls Refresh. Data can also appear to change if another user changes a record after you originally fetched the data and before you call Refresh.


Displaying data as labels

Section topics 	See also

TDBText is a read-only control similar to the TLabel component on the Standard page of the Component palette. A TDBText control is useful when you want to provide display-only data on a form that allows user input in other controls. For example, suppose a form is created around the fields in a customer list table, and that once the user enters a street address, city, and state or province information in the form, you use a dynamic lookup to automatically determine the zip code field from a separate table. A TDBText component tied to the zip code table could be used to display the zip code field that matches the address entered by the user.

TDBText gets the text it displays from a specified field in the current record of a dataset. Because TDBText gets its text from a dataset, the text it displays is dynamic--the text changes as the user navigates the database table. Therefore you cannot specify the display text of TDBText at design time as you can with TLabel.

Note:	When you place a TDBText component on a form, make sure its AutoSize property is True (the default) to ensure that the control resizes itself as necessary to display data of varying widths. If AutoSize is set to False, and the control is too small, data display is truncated.


Displaying and editing fields in an edit box

Section topics 	See also

TDBEdit is a data-aware version of an edit box component. TDBEdit displays the current value of a data field to which it is linked and permits it to be edited using standard edit box techniques.

For example, suppose CustomersSource is a TDataSource component that is active and linked to an open TTable called CustomersTable. You can then place a TDBEdit component on a form and set its properties as follows:

DataSource: CustomersSource

DataField: CustNo

The data-aware edit box component immediately displays the value of the current row of the CustNo column of the CustomersTable dataset, both at design time and at run time.


Displaying and editing text in a memo control

Section topics 	See also

TDBMemo is a data-aware component--similar to the standard TMemo component--that can display binary large object (BLOB) data. TDBMemo displays multi-line text, and permits a user to enter multi-line text as well. You can use TDBMemo controls to display memo fields from dBASE and Paradox tables and text data contained in BLOB fields.

By default, TDBMemo permits a user to edit memo text. To prevent editing, set the ReadOnly property of the memo control to True. To display tabs and permit users to enter them in a memo, set the WantTabs property to True. To limit the number of characters users can enter into the database memo, use the MaxLength property. The default value for MaxLength is 0, meaning that there is no character limit.

Several properties affect how the database memo appears and how text is entered. You can supply scroll bars in the memo with the ScrollBars property. To prevent word wrap, set the WordWrap property to False. The Alignment property determines how the text is aligned within the control. Possible choices are taLeftJustify (the default), taCenter, and taRightJustify. To change the font of the text, use the Font property.

At run time, users can cut, copy, and paste text to and from a database memo control. You can accomplish the same task programmatically by using the CutToClipboard, CopyToClipboard, and PasteFromClipboard methods.

Because the TDBMemo can display large amounts of data, it can take time to populate the display at run time. To reduce the time it takes to scroll through data records, TDBMemo has an AutoDisplay property that controls whether the accessed data should ve displayed automatically. If you set AutoDisplay to False, TDBMemo displays the field name rather than actual data. Double-click inside the control to view the actual data.


Displaying and editing text in a rich edit memo control

Section topics 	See also

TDBRichEdit is a data-aware component--similar to the standard TRichEdit component--that can display binary large object (BLOB) data. TDBMemo displays formatted, multi-line text, and permits a user to enter formatted multi-line text as well. You can use TDBRichEdit controls to display memo fields from dBASE and Paradox tables and text data contained in BLOB fields.

Note:	While TDBRichEdit provides properties and methods to enter and work with rich text, it does not provide any user interface components to make these formatting options available to the user. Your application must implement the user interface to surface rich text capabilities.

By default, TDBRichEdit permits a user to edit memo text. To prevent editing, set the ReadOnly property of the rich edit control to True. To display tabs and permit users to enter them in a memo, set the WantTabs property to True. To limit the number of characters users can enter into the database memo, use the MaxLength property. The default value for MaxLength is 0, meaning that there is no character limit.

Because the TDBRichEdit can display large amounts of data, it can take time to populate the display at run time. To reduce the time it takes to scroll through data records, TDBRichEdit has an AutoDisplay property that controls whether the accessed data should be displayed automatically. If you set AutoDisplay to False, TDBRichEdit displays the field name rather than actual data. Double-click inside the control to view the actual data.


Displaying and editing graphics fields in an image control

Section topics 	See also

TDBImage is a data-aware component that displays bitmapped graphics contained in BLOB data fields. It captures BLOB graphics images from a dataset, and stores them internally in the Windows .DIB format.

By default, TDBImage permits a user to edit a graphics image by cutting and pasting to and from the Clipboard using the CutToClipboard, CopyToClipboard, and PasteFromClipboard methods.You can, instead, supply your own editing methods attached to the event handlers for the control.

By default, an image control displays as much of a graphic as fits in the control. You can set the Stretch property to True to resize the graphic to fit within an image control as it is resized.

Because the TDBImage can display large amounts of data, it can take time to populate the display at run time. To reduce the time it takes scroll through data records, TDBImage has an AutoDisplay property that controls whether the accessed data should automatically displayed. If you set AutoDisplay to False, TDBImage displays the field name rather than actual data. Double-click inside the control to view the actual data.


Displaying and editing data in list and combo boxes

Section topics 	See also

There are four data controls that provide data-aware versions of standard list box and combo box controls. These useful controls provide the user with a set of default data values to choose from at run time.

Note:	Data-aware list and combo box can be linked only to data sources for table components. They do not work with query components.

The following table describes these controls.

Data control	Description
TDBListBox	Displays a list of items from which a user can update a field in the current record. The list of display items is a property of the control.
TDBComboBox	Combines an edit box with a list box. A user can update a field in the current record by choosing a value from the drop-down list or by entering a value. The list of display items is a property of the control.
TDBLookupListBox	Displays a list of items from which a user can update a column in the current record. The list of display items is looked up in another dataset.
TDBLookupComboBox	Combines an edit box with a list box. A user can update a field in the current record by choosing a value from the drop-down list or by entering a value. The list of display items is looked up in another dataset.


Displaying and editing data in a list box

Section topics 	See also

TDBListBox displays a scrollable list of items from which a user can choose to enter in a data field. A data-aware list box displays a default value for a field in the current record and highlights its corresponding entry in the list. If the current row's field value is not in the list, no value is highlighted in the list box. When a user selects a list item, the corresponding field value is changed in the underlying dataset.

Use the String List editor at design time to create the list of items to display in the Items property. The Height property determines how many items are visible in the list box at one time. The IntegralHeight property controls the way the list box is displayed. If IntegralHeight is False (the default), the bottom of the list box is determined by the ItemHeight property, and the bottom item might not be completely displayed. If IntegralHeight is True the visible bottom item in the list box is fully displayed.


Displaying and editing data in a combo box

Section topics 	See also

The TDBComboBox control combines the functionality of a data-aware edit control and a drop-down list. At run time it can display a drop-down list from which a user can pick from a predefined set of values, and it can permit a user to enter an entirely different value.

The Items property of the component specifies the items contained in the drop-down list. At design time, use the String List Editor to populate the Items list. At run time, use the methods of the Items property to manipulate its string list.

When a control is linked to a field through its DataField property, it displays the value for the field in the current row, regardless of whether it appears in the Items list. The Style property determines user interaction with the control. By default, Style is csDropDown, meaning a user can enter values from the keyboard, or choose an item from the drop-down list. The following properties determine how the Items list is displayed at run time:

Style determines the display style of the component:

csDropDown (default): Displays a drop-down list with an edit box in which the user can enter text. All items are strings and have the same height.

csSimple: Does not display the drop-down list.

csDropDownList: Displays a drop-down list and edit box, but the user cannot enter or change values that are not in the drop-down list at run time.

csOwnerDrawFixed and csOwnerDrawVariable: Allows the items list to display values other than strings (for example, bitmaps).

DropDownCount: the maximum number of items displayed in the list. If the number of Items is greater than DropDownCount, the user can scroll the list. If the number of Items is less than DropDownCount, the list will be just large enough to display all the Items.

ItemHeight: The height of each item when style is csOwnerDrawFixed.

Sorted: If True, then the Items list is displayed in alphabetical order.


Displaying and editing data in lookup list and combo boxes

Section topics 	See also

TDBLookupListBox and TDBLookupComboBox are data-aware controls that derive a list of display items from one of two sources:

Lookup field defined for a dataset.

Secondary data source, data field, and key.

In either case, a user is presented with a restricted list of choices from which to set a valid field value. When a user selects a list item, the corresponding field value is changed in the underlying dataset.

For example, consider an order form whose fields are tied to the OrdersTable. OrdersTable contains a CustNo field corresponding to a customer ID, but OrdersTable does not have any other customer information. The CustomersTable, on the other hand, contains a CustNo field corresponding to a customer ID, and also contains additional information, such as the customer's company and mailing address. It would be convenient if the order form enabled a clerk to select a customer by company name instead of customer ID when creating an invoice. A TDBLookupListBox that displays all company names in CustomersTable enables a user to select the company name from the list, and set the CustNo on the order form appropriately.


Specifying a list based on a lookup field

Section topics 	See also

To specify list box items using a lookup field, the dataset to which you link the control must already define a lookup field. For more information about defining a lookup field for a dataset, see "Defining a lookup field."

To specify a lookup field for the list box items:

1.	Set the DataSource property of the list box to the data source for the dataset containing the lookup field to use.

2.	Choose the lookup field to use from the drop-down list for the DataField property.

When you activate a table associated with a lookup list box control, the control recognizes that its data field is a lookup field, and displays the appropriate values from the lookup.


Specifying a list based on a secondary data source

Section topics 	See also

If you have not defined a lookup field for a dataset, you can establish a similar relationship using a secondary data source, a field value to search on in the secondary data source, and a field value to return as a list item.

To specify a secondary data source for list box items:

1.	Set the DataSource property of the list box to the data source for the control.

2.	Choose a field into which to insert looked-up values from the drop-down list for the DataField property. The field you choose cannot be a lookup field.

3.	Set the ListSource property of the list box to the data source for the dataset that contain the field whose values you want to look up.

4.	Choose a field to use as a lookup key from the drop-down list for the KeyField property. The drop-down list displays fields for the dataset associated with data source you specified in Step 3. The field you choose need not be part of an index, but if it is, lookup performance is even faster.

5.	Choose a field whose values to return from the drop-down list for the ListField property. The drop-down list displays fields for the dataset associated with the data source you specified in Step 3.

When you activate a table associated with a lookup list box control, the control recognizes that its list items are derived from a secondary source, and displays the appropriate values from that source.


Setting lookup list and combo box properties

Section topics 	See also

The following table lists important properties for lookup list and combo boxes.

Property	Purpose
DataField	Specifies the field in the master dataset which provides the key value to be looked up in the lookup dataset. This field is modified when a user selects a list box or combo box item. If DataField is set to a lookup field, the KeyField, ListField, and ListSource properties are not used.
DataSource	Specifies a data source for the control.  If the selection in the control is changed, this dataset is placed in dsEdit mode.
KeyField	Specifies the field in the lookup dataset corresponding to DataField.  The control searches for the DataField value in the KeyField of the lookup dataset.  The lookup dataset should have an index on this field to facilitate lookups.
ListField	Specifies the field of the lookup dataset to display in the control.
ListSource	Specifies a data source for the secondary (lookup) dataset. The sort order of items displayed in the list box or combo box is determined by the index specified by the IndexName property of the lookup dataset. That index need not be the same one used by the KeyField property.
RowCount	TDBLookupListBox only. Specifies the number of lines of text to display in the list box. The height of the list box is adjusted to fit this row count exactly.
DropDownRows	TDBLookupComboBox only. Specifies the number of lines of text to display in the drop-down list.


Searching incrementally for list item values

Section topics 	See also

At run time, users can use an incremental search to find list box items. When the control has focus, for example, typing 'ROB' selects the first item in the list box beginning with the letters 'ROB'. Typing an additional 'E' selects the first item starting with 'ROBE', such as 'Robert Johnson'. The search is case-insensitive. Backspace and Esc cancel the current search string (but leave the selection intact), as does a two second pause between keystrokes.


Handling Boolean field values with check boxes

Section topics 	See also

TDBCheckBox is a data-aware check box control. It can be used to set the values of Boolean fields in a dataset. For example, a customer invoice form might have a check box control that when checked indicates the customer is tax-exempt, and when unchecked indicates that the customer is not tax-exempt.

The data-aware check box control manages its checked or unchecked state by comparing the value of the current field to the contents of ValueChecked and ValueUnchecked properties. If the field value matches the ValueChecked property, the control is checked. Otherwise, if the field matches the ValueUnchecked property, the control is unchecked.

Note:	The values in ValueChecked and ValueUnchecked cannot be identical.

Set the ValueChecked property to a value the control should post to the database if the control is checked when the user moves to another record. By default, this value is set to "True," but you can make it any alphanumeric value appropriate to your needs. You can also enter a semicolon-delimited list of items as the value of ValueChecked. If any of the items matches the contents of that field in the current record, the check box is checked. For example, you can specify a ValueChecked string like:

DBCheckBox1.ValueChecked := 'True;Yes;On';

If the field for the current record contains values of "True," "Yes," or "On," then the check box is checked. Comparison of the field to ValueChecked strings is case-insensitive. If a user checks a box for which there are multiple ValueChecked strings, the first string is the value that is posted to the database.

Set the ValueUnchecked property to a value the control should post to the database if the control is not checked when the user moves to another record. By default, this value is set to "False," but you can make it any alphanumeric value appropriate to your needs. You can also enter a semicolon-delimited list of items as the value of ValueUnchecked. If any of the items matches the contents of that field in the current record, the check box is unchecked.

A data-aware check box is disabled whenever the field for the current record does not contain one of the values listed in the ValueChecked or ValueUnchecked properties.

If the field with which a check box is associated is a logical field, the check box is always checked if the contents of the field is True, and it is unchecked if the contents of the field is False. In this case, strings entered in the ValueChecked and ValueUnchecked properties have no effect on logical fields.


Restricting field values with radio controls

Section topics 	See also

TDBRadioGroup is a data-aware version of a radio group control. It enables you to set the value of a data field with a radio button control where there is a limited number of possible values for the field. The radio group consists of one button for each value a field can accept. Users can set the value for a data field by selecting the desired radio button.

The Items property determines the number of radio buttons that appear in the group. Items is a string list. One radio button is displayed for each string in Items, and each string appears to the right of a radio button as the button's label.

If the current value of a field associated with a radio group matches one of the strings in the Items property, that radio button is selected. For example, if three strings, "Red," "Yellow," and "Blue," are listed for Items, and the field for the current record contains the value "Blue," then the third button in the group is selected.

Note:	If the field does not match any strings in Items, a radio button may still be selected if the field matches a string in the Values property. If the field for the current record does not match any strings in Items or Values, no radio button is selected.

The Values property can contain an optional list of strings that can be returned to the dataset when a user selects a radio button and posts a record. Strings are associated with buttons in numeric sequence. The first string is associated with the first button, the second string with the second button, and so on. For example, suppose Items contains "Red," "Yellow," and "Blue," and Values contains "Magenta," "Yellow," and "Cyan." If a user selects the button labeled "Red," "Magenta" is posted to the database.

If strings for Values are not provided, the Item string for a selected radio button is returned to the database when a record is posted.


Navigating and manipulating records

Section topics 	See also

TDBNavigator provides users a simple control for navigating through records in a dataset, and for manipulating records. The navigator consists of a series of buttons that enable a user to scroll forward or backward through records one at a time, go to the first record, go to the last record, insert a new record, update an existing record, post data changes, cancel data changes, delete a record, and refresh record display.

The following figure shows the navigator that appears by default when you place it on a form at design time. The navigator consists of a series of buttons that let a user navigate from one record to another in a dataset, and edit, delete, insert, and post records. The VisibleButtons property of the navigator enables you to hide or show a subset of these buttons dynamically.

The following table describes the buttons on the navigator.

Button	Purpose
First	Calls the dataset's First method to set the current record to the first record.
Prior	Calls the dataset's Prior method to set the current record to the previous record.
Next	Calls the dataset's Next method to set the current record to the next record.
Last	Calls the dataset's Last method to set the current record to the last record.
Insert	Calls the dataset's Insert method to insert a new record before the current record, and set the dataset in Insert state.
Delete	Deletes the current record. If the ConfirmDelete property is True it prompts for confirmation before deleting.
Edit	Puts the dataset in Edit state so that the current record can be modified.
Post	Writes changes in the current record to the database.
Cancel	Cancels edits to the current record, and returns the dataset to Browse state.
Refresh	Clears data control display buffers, then refreshes its buffers from the physical table or query. Useful if the underlying data may have been changed by another application.


Choosing navigator buttons to display

Section topics 	See also

When you first place a TDBNavigator on a form at design time, all its buttons are visible. You can use the VisibleButtons property to turn off buttons you do not want to use on a form. For example, on a form that is intended for browsing rather than editing, you might want to disable the Edit, Insert, Delete, Post, and Cancel buttons.

Hiding and showing navigator buttons at design time

The VisibleButtons property in the Object Inspector is displayed with a + sign to indicate that it can be expanded to display a Boolean value for each button on the navigator. To view and set these values, double-click the VisibleButtons property. The list of buttons that can be turned on or off appears in the Object Inspector below the VisibleButtons property. The + sign changes to a - (minus) sign, indicating that you can collapse the list of properties by double-clicking the VisibleButtons property.

Button visibility is indicated by the Boolean state of the button value. If a value is set to True, the button appears in the TDBNavigator. If False, the button is removed from the navigator at design and run times.

Note:	As button values are set to False, they are removed from the TDBNavigator on the form, and the remaining buttons are expanded in width to fill the control. You can drag the control's handles to resize the buttons.

For more information about buttons and the methods they call, see the online VCL Reference.

Hiding and showing navigator buttons at run time

At run time you can hide or show navigator buttons in response to user actions or application states. For example, suppose you provide a single navigator for navigating through two different datasets, one of which permits users to edit records, and the other of which is read-only. When you switch between datasets, you want to hide the navigator's Insert, Delete, Edit, Post, Cancel, and Refresh buttons for the read-only dataset, and show them for the other dataset.

For example, suppose you want to prevent edits to the OrdersTable by hiding the Insert, Delete, Edit, Post, Cancel, and Refresh buttons on the navigator, but that you also want to allow editing for the CustomersTable. The VisibleButtons property controls which buttons are displayed in the navigator. Here's one way you might code the OnEnter event handler:

procedure TForm1.CustomerCompanyEnter(Sender :TObject);

begin

 if Sender = CustomerCompany then

 begin

   DBNavigatorAll.DataSource := CustomerCompany.DataSource;

   DBNavigatorAll.VisibleButtons := [nbFirst,nbPrior,nbNext,nbLast];

 end

 else

 begin

   DBNavigatorAll.DataSource := OrderNum.DataSource;

   DBNavigatorAll.VisibleButtons := DBNavigatorAll.VisibleButtons + [nbInsert,

     nbDelete,nbEdit,nbPost,nbCancel,nbRefresh];

 end;

end;


Displaying fly-over help

Section topics 	See also

To display fly-over help for each navigator button at run time, set the navigator ShowHints property to True. When ShowHints is True, the navigator displays fly-by Help Hints whenever you pass the mouse cursor over the navigator buttons. ShowHints is False by default.

The Hints property controls the fly-over help text for each button. By default Hints is an empty string list. When Hints is empty, Delphi displays default help text for each button. To provide customized fly-over help for the navigator buttons, use the String list editor to enter a separate line of hint text for each button in the Hints property. When present, the strings you provide override the default hints provided by the navigator control.


Using a single navigator for multiple datasets

Section topics 	See also

As with other data-aware controls, a navigator's DataSource property specifies the data source that links the control to a dataset. By changing a navigator's DataSource property at run time, a single navigator can provide record navigation and manipulation for multiple datasets.

Suppose a form contains two edit controls linked to the CustomersTable and OrdersTable datasets through the CustomersSource and OrdersSource data sources respectively. When a user enters the edit control connected to CustomersSource, the navigator should also use CustomersSource, and when the user enters the edit control connected to OrdersSource, the navigator should switch to OrdersSource as well. You can code an OnEnter event handler for one of the edit controls, and then share that event with the other edit control. For example:

procedure TForm1.CustomerCompanyEnter(Sender :TObject);

begin

 if Sender = CustomerCompany then

   DBNavigatorAll.DataSource := CustomerCompany.DataSource

 else

   DBNavigatorAll.DataSource := OrderNum.DataSource;

end;


See also

Data Access components

Data Controls components


Working with grids

Section topics 	See also

These topics describe how to use the data-aware TDBGrid control to display and edit dataset records in a tabular grid format, similar to a spreadsheet or the tabular view of data available in most desktop database applications such as Paradox and dBASE. Your application can use the data grid to edit, insert, delete, and view records in a dataset. The availability and appearance of columns and data in a grid can be controlled by creating persistent columns for display at both design time and run time.

Note:	Grid controls inherit the common functionality of other data-aware controls. This common functionality is described in "Using data controls."

These topics also introduce another data-aware grid control, TDBCtrlGrid, available in the Delphi Client/Server and Enterprise suites, that enables you to display other data-aware components as repeating units within a grid-like structure.


Viewing and editing data with TDBGrid

Section topics 	See also

A TDBGrid control enables you to view and edit records in a dataset in a tabular grid format

The TDBGrid control:

Two factors affect the appearance of records displayed in a grid control:

Existence of persistent column objects defined for the grid using the Columns editor. Persistent column objects provide great flexibility setting grid and data appearance.

Creation of persistent field components for the dataset displayed in the grid. For more information about creating persistent field components using the Fields editor, see "Working with field components."

A grid control has a Columns property that is itself a wrapper on a TDBGridColumns object. TDBGridColumns is a collection of TColumn objects representing all of the columns in a grid control. You can use the Columns editor to set up column attributes at design time, or use the Columns property of the grid to access the properties, events, and methods of TDBGridColumns at run time.

The State property of a grid's Columns property indicates if persistent column objects exist for the grid. Columns.State is a run-time-only property that is automatically set for a grid. The default state is csDefault, meaning that persistent column objects do not exist for the grid. In that case, the display of data in the grid is determined either by persistent field components for the dataset displayed in the grid, or for datasets without persistent field components, by Delphi's default set of criteria for data display.


Using a grid control in its default state

Section topics 	See also

If a grid's Columns.State property is csDefault, the appearance of records is determined primarily by the properties of the fields in the grid's dataset. Grid columns are dynamically generated from the visible fields of the dataset, and the order of columns in the grid matches the order of fields in the dataset. Every column in the grid is associated with a field component. Property changes to field components immediately show up in the grid.

Using a grid control with dynamically-generated columns is useful for viewing and editing the contents of arbitrary tables selected at run time. Because the grid's structure is not set, it can change dynamically to accommodate different datasets. A single grid with dynamically-generated columns can display a Paradox table at one moment, then switch to display the results of an SQL query when the grid's DataSource property changes or when the DataSet property of the data source itself is changed.

You can change the appearance of a dynamic column at design time or run time, but what you are actually modifying are the corresponding properties of the field component displayed in the column. Properties of dynamic columns exist only so long as a column is associated with a particular field in a single dataset. For example, changing the Width property of a column changes the DisplayWidth property of the field associated with that column. Changes made to column properties that are not based on field properties, such as Font, exist only for the lifetime of the column.

Properties of dynamic columns are retained for as long as the associated field component exists. If a grid's dataset consists of dynamic field components, the fields are destroyed each time the dataset is closed. When the field components are destroyed, all dynamic columns associated with them are destroyed as well. If a grid's dataset consists of persistent field components, the field components exist even when the dataset is closed, so the columns associated with those fields also retain their properties when the dataset is closed.

Note:	Changing a grid's Columns.State property to csDefault at run time deletes all column objects in the grid (even persistent columns), and rebuilds dynamic columns based on the visible fields of the grid's dataset.


Creating a customized grid

Section topics 	See also

A customized grid control is one for which you define persistent column objects that describe how a column appears and how the data in the column is displayed. A customized grid enables you to configure multiple grids to present different views of the same dataset (different column orders, different field choices, and different column colors and fonts, for example). A customized grid also enables you to let users modify the appearance of the grid at run time without affecting the fields used by the grid or the field order of the dataset.

Customized grids are best used with datasets whose structure is known at design time. Because they expect field names established at design time to exist in the dataset, customized grids are not well suited to browsing arbitrary tables selected at run time.

Understanding persistent columns

When you create persistent column objects for a grid, they are only loosely associated with underlying fields in a grid's dataset. Default property values for persistent columns are dynamically fetched from a default source (such as the grid or associated field) until a value is assigned to the column property. Until you assign a column property a value, its value changes as its default source changes.

For example, the default source for a column title caption is an associated field's DisplayLabel property. If you modify the DisplayLabel property, the column title reflects that change immediately.

Once you assign a value to a column property, it no longer changes when its default source changes. For example, if you assign a string to the column title's caption, the title caption is independent of the associated field's DisplayLabel property. Subsequent changes to the field's DisplayLabel property no longer affect the column's title.

Persistent columns exist independently from field components with which they are associated. In fact, persistent columns do not have to be associated with field objects at all. If a persistent column's FieldName property is blank, or if the field name does not match the name of any field in the grid's current dataset, the column's Field property is nil and the column is drawn with blank cells. You can use a blank column to display bitmaps or bar charts that graphically depict some aspect of a record's data in an otherwise blank cell, for example. To do so, you must override the cells' default drawing method.

Two or more persistent columns can be associated with the same field in a dataset. For example, you might display a part number field at the left and right extremes of a wide grid to make it easier to find the part number without having to scroll the grid.

Note:	Because persistent columns do not have to be associated with a field in a dataset, and because multiple columns can reference the same field, a customized grid's FieldCount property can be less than or equal to the grid's column count. Also note that if the currently selected column in a customized grid is not associated with a field, the grid's SelectedField property is nil and the SelectedIndex property is -1.

Persistent columns can be configured to display grid cells as a combo-box drop-down list of lookup values from another dataset or from a static pick list, or as an ellipsis button (…) in a cell that can be clicked upon to launch special data viewers or dialogs related to the current cell.

Determining the source of a column property at run time

At run time you can test a column's AssignedValues property to determine whether a column property gets its values from an associated field component, or is assigned its own value.

You can reset all default properties for a single column by calling the column's RestoreDefaults method. You can also reset default properties for all columns in a grid by calling the column list's RestoreDefaults method:

DBGrid1.Columns.RestoreDefaults;

To add a persistent column call the Add method for the column list:

DBGrid1.Columns.Add;

You can delete a persistent column by simply freeing the column object:

DBGrid1.Columns[5].Free;

Finally, assigning csCustomized to the Column.State property for a grid at run time puts the grid into customized mode. Any existing columns in the grid are destroyed and new persistent columns are built for each field in the grid's dataset.


Creating persistent columns

Section topics 	See also

To customize the appearance of grid at design time, you invoke the Columns editor to create a set of persistent column objects for the grid. At run time, the State property for a grid with persistent column objects is automatically set to csCustomized.

To create persistent columns for a grid control:

1.	Select the grid component in the form.

2.	Invoke the Columns editor by double clicking on the grid's Columns property in the Object Inspector.

The Columns list box displays the persistent columns that have been defined for the selected grid. When you first bring up the Columns editor, this list is empty because the grid is in its default state, containing only dynamic columns.

You can create persistent columns for all fields in a dataset at once, or you can create persistent columns on an individual basis.

To create persistent columns for all fields:

1.	Right-click the grid to invoke the context menu and choose Add All Fields. Note that if the grid is not already associated with a data source, Add All Fields is disabled. Associate the grid with a data source that has an active dataset before choosing Add All Fields.

2.	If the grid already contains persistent columns, a dialog box asks if you want to delete the existing columns, or append to the column set. If you choose Yes, any existing persistent column information is removed, and all fields in the current dataset are inserted by field name according to their order in the dataset. If you choose No, any existing persistent column information is retained, and new column information, based on any additional fields in the dataset, are appended to the dataset.

3.	Click Close to apply the persistent columns to the grid and close the dialog box.

To create persistent columns individually:

1.	Choose the Add button in the Columns editor. The new column will be selected in the list box. The new column is given a sequential number and default name (for example, 0 - TColumn).

2.	To associate a field with this new column, set the FieldName property in the Object Inspector.

3.	To set the title for the new column, set the Caption option for the Title property in the Object Inspector.

4.	Close the Columns editor to apply the persistent columns to the grid and close the dialog box.

Deleting persistent columns

Deleting a persistent column from a grid is useful for eliminating fields that you do not want to display. To remove a persistent column from a grid:

1.	Select the field to remove in the Columns list box.

2.	Click Delete (you can also use the context menu or Del key, to remove a column).

Note:	If you delete all the columns from a grid, the Columns.State property reverts to its csDefault state and automatically build dynamic columns for each field in the dataset.

Arranging the order of persistent columns

The order in which columns appear in the Columns editor is the same as the order the columns appear in the grid. You can change the column order by dragging and dropping columns within the Columns list box.

To change the order of a column:

1.	Select the column in the Columns list box.

2.	Drag it to a new location in the list box.

You can also change the column order by dragging the column in the actual grid, just as you can at run time.

Defining a lookup list column

To make a column display a drop-down list of values drawn from a separate lookup table, you must define a lookup field object in the dataset. For more information about creating lookup fields, see "Working with field components."

Once the lookup field is defined, set the column's FieldName to the lookup field name and make sure the column's ButtonStyle is set to cbsAuto. The grid automatically displays a combo box-like drop-down button when a cell of that column is in edit mode. The drop-down list is populated with lookup values defined by the lookup field.

Defining a pick list column

A pick list column looks and operates like a lookup list column, except that the column's field is a normal field and the drop-down list is populated with the list of values in the column's PickList property instead of from a lookup table.

To define a pick list column:

1.	Select the column in the Columns list box.

2.	Set ButtonStyle to cbsAuto.

3.	Double-click the Picklist property in the Object Inspector to bring up a string list editor.

In the String List editor, enter the list of values you want to appear in the drop-down list for this column. If the pick list contains data, the column becomes a pick list column.

Note:	To restore a column to its normal behavior, delete all the text from the Pick list editor.

Putting a button in a column

A column can display an ellipsis button (…) to the right of the normal cell editor. Ctrl+Enter or a mouse click fires the grid's OnEditButtonClick event. You can use the ellipsis button to bring up forms containing more detailed views of the data in the column. For example, in a table that displays summaries of invoices, you could set up an ellipsis button in the invoice total column to bring up a form that displays the items in that invoice, or the tax calculation method, and so on. For graphic fields, you could use the ellipsis button to bring up a form which displays an image.

To create an ellipsis button in a column:

1.	Select the column in the Columns list box.

2.	Set ButtonStyle to cbsEllipsis.

3.	Write an OnEditButtonClick event handler.

Setting column properties at design time

Column properties determine how data is displayed in the cells of that column. Most column properties obtain their default values from properties associated with another component, called the default source, such as a grid or an associated field component.

To set a column's properties:

1.	Select the column in the Columns editor

2.	Set its properties in the Object Inspector.

The following table summarizes key column properties you can set.

Property	Purpose
Alignment	Left justifies, right justifies, or centers the field data in the column. Default source:  TField.Alignment.
ButtonStyle	cbsAuto:  (default) Displays a drop-down list if the associated field is a lookup field, or if the column's PickList property contains data.cbsEllipsis:  Displays an ellipsis (...) button to the right of the cell.  Clicking on the button fires the grid's OnEditButtonClick event.cbsNone:  The column uses only the normal edit control to edit data in the column.
Color	Specifies the background color of the cells of the column.  For text foreground color, see the font property. Default Source:  TDBGrid.Color
DropDownRows	The number of lines of text displayed by the drop-down list. Default: 7.
FieldName	Specifies the field name that is associated with this column.  This can be blank.
ReadOnly	True:  The data in the column cannot be edited by the user.False: (default)  The data in the column can be edited.
Width	Specifies the width of the column in screen pixels. Default Source: derived from TField.DisplayWidth.
Font	Specifies the font type, size, and color used to draw text in the column. Default Source: TDBGrid.Font.
PickList	Contains a list of values to display in a drop-down list in the column.
Title	Sets properties for the title of the selected column.

The following table summarizes the options you can specify for the Title property.

Property	Purpose
Alignment	Left justifies (default), right justifies, or centers the caption text in the column title.
Caption	Specifies the text to display in the column title. Default Source:  TField.DisplayLabel.
Color	Specifies the background color used to draw the column title cell. Default Source: TDBGrid.FixedColor.
Font	Specifies the font type, size, and color used to draw text in the column title. Default Source: TDBGrid.TitleFont.
Restoring default values to a column

You can undo property changes made to one or more columns. In the Columns editor, select the column or columns to restore, and then select Restore Defaults from the context menu. Restore defaults discards assigned property settings and restores a column's properties to those derived from its underlying field component.


Setting grid options

Section topics 	See also

You can use the grid Options property at design time to control basic grid behavior and appearance at run time. When a grid component is first placed on a form at design time, the Options property in the Object Inspector is displayed with a + (plus) sign to indicate that the Options property can be expanded to display a series of Boolean properties that you can set individually.

To view and set these properties, double-click the Options property. The list of options that you can set appears in the Object Inspector below the Options property. The + sign changes to a - (minus) sign, indicating that you can collapse the list of properties by double-clicking the Options property.

The following table lists the Options properties that can be set, and describes how they affect the grid at run time.

Option	Purpose
dgEditing	True: (Default). Enables editing, inserting, and deleting records in the grid.False: Disables editing, inserting, and deleting records in the grid.
dgAlwaysShowEditor	True: When a field is selected, it is in Edit state.False: (Default). A field is not automatically in Edit state when selected.
dgTitles	True: (Default). Displays field names across the top of the grid.False: Field name display is turned off.
dgIndicator	True: (Default). The indicator column is displayed at the left of the grid, and the current record indicator (an arrow at the left of the grid) is activated to show the current record. On insert, the arrow becomes an asterisk. On edit, the arrow becomes an I-beam.False: The indicator column is turned off.
dgColumnResize	True: (Default). Columns can be resized by dragging the column rulers in the title area. Resizing changes the corresponding width of the underlying TField component.False: Columns cannot be resized in the grid.
dgColLines	True: (Default). Displays vertical dividing lines between columns.False: Does not display dividing lines between columns.
dgRowLines	True: (Default). Displays horizontal dividing lines between records.False: Does not display dividing lines between records.
dgTabs	True: (Default). Enables tabbing between fields in records.False: Tabbing exits the grid control.
dgRowSelect	True: The selection bar spans the entire width of the grid.False: (Default). Selecting a field in a record selects only that field.
dgAlwaysShowSelection	True: (Default). The selection bar in the grid is always visible, even if another control has focus.False: The selection bar in the grid is visible only when the grid has focus.
dgConfirmDelete	True: (Default). Prompt for confirmation to delete records (Ctrl+Del).False: Delete records without confirmation.
dgCancelOnExit	True: (Default). Cancels a pending insert when focus leaves the grid. This option prevents inadvertent posting of partial or blank records.False: Permits pending inserts.
dgMultiSelect	True: Allows user to select noncontiguous rows in the grid using Ctrl-Shift or Shift+ arrow keys.False: (Default). Does not allow user to multi-select rows.


Editing in the grid

Section topics 	See also

At run time, you can use a grid to modify existing data and enter new records, if the following default conditions are met:

The CanModify property of the Dataset is True.

The ReadOnly property of grid is False.

When a user edits a record in the grid, changes to each field are posted to an internal record buffer, but are not posted until the user moves to a different record in the grid. Even if focus is changed to another control on a form, the grid does not post changes until another the cursor for the dataset is moved to another record. When a record is posted, Delphi checks all data-aware components associated with the dataset for a change in status. If there is a problem updating any fields that contain modified data, Delphi raises an exception, and does not modify the record.

You can cancel all edits for a record by pressing Esc in any field before moving to another record.


Rearranging column order at design time

Section topics 	See also

In grid controls with persistent columns, and default grids whose datasets contain persistent fields, you can reorder the grid columns at design time by clicking on the title cell of a column and dragging it to its new location in the grid.

Note:	Reordering persistent fields in the Fields editor also reorders columns in a default grid, but not a custom grid.

Important:	You cannot reorder columns in grids containing both dynamic columns and dynamic fields at design time, since there is nothing persistent to record the altered field or column order.


Rearranging column order at run time

Section topics 	See also

At run time, a user can use the mouse to drag a column to a new location in the grid if its DragMode property is set to dmManual. Reordering the columns of a grid with a State property of csDefault state also reorders the field components in the dataset underlying the grid. The order of fields in the physical table is not affected.

A grid's OnColumnMoved event is fired after a column has been moved.

To prevent a user from rearranging columns at run time, set the grid's DragMode property to dmAutomatic.


Controlling grid drawing

Section topics 	See also

Your first level of control over how a grid control draws itself is setting column properties. The grid automatically uses the font, color, and alignment properties of a column to draw the cells of that column. The text of data fields is drawn using the DisplayFormat or EditFormat properties of the field component associated with the column.

You can augment the default grid display logic with code in a grid's OnDrawColumnCell event. If the grid's DefaultDrawing property is True, all the normal drawing is performed before your OnDrawColumnCell event handler is called. Your code can then draw on top of the default display. This is primarily useful when you have defined a blank persistent column and want to draw special graphics in that column's cells.

If you want to replace the drawing logic of the grid entirely, set DefaultDrawing to False and place your drawing code in the grid's OnDrawColumnCell event. If you want to replace the drawing logic only in certain columns or for certain field data types, you can call the DefaultDrawColumnCell inside your OnDrawColumnCell event handler to have the grid use its normal drawing code for selected columns. This reduces the amount of work you have to do if you only want to change the way Boolean field types are drawn, for example.


Responding to user actions at run time

Section topics 	See also

To modify grid behavior, write event handlers to respond to specific actions within the grid at run time. Because a grid typically displays many fields and records at once, you may have very specific needs to respond to changes to individual columns. For example, you might want to activate and deactivate a button elsewhere on the form every time a user enters and exits a specific column.

The following table lists the grid events available in the Object Inspector:

Event	Purpose
OnCellClick	Specify the action to take when a user clicks on a cell in the grid.
OnColEnter	Specify action to take when a user moves into a column on the grid.
OnColExit	Specify action to take when a user leaves a column on the grid.
OnColumnMoved	Called when the user moves a column to a new location.
OnDblClick	Specify action to take when a user double clicks in the grid.
OnDragDrop	Specify action to take when a user drags and drops in the grid.
OnDragOver	Specify action to take when a user drags over the grid.
OnDrawColumnCell	Called to draw individual cells.
OnDrawDataCell	(obsolete) In grids with State = csDefault, called to draw individual cells.
OnEditButtonClick	Called when the user clicks on an elipsis button in a column.
OnEndDrag	Specify action to take when a user stops dragging on the grid.
OnEnter	Specify action to take when the grid gets focus.
OnExit	Specify action to take when the grid loses focus.
OnKeyDown	Specify action to take when a user presses any key or key combination on the keyboard when in the grid.
OnKeyPress	Specify action to take when a user presses a single alphanumeric key on the keyboard when in the grid.
OnKeyUp	Specify action to take when when a user releases a key when in the grid.
OnStartDrag	Specify the action to take when a user starts dragging on the grid.
OnTitleClick	Specify the action to take when a user clicks the title for a column.

There are many uses for these events. For example, you might write a handler for the OnDblClick event that pops up a list from which a user can choose a value to enter in a column. Such a handler would use the SelectedField property to determine to current row and column.


Creating a grid that contains other data-aware controls

Section topics 	See also

A TDBCtrlGrid control displays multiple fields in multiple records in a tabular grid format. Each cell in a grid displays multiple fields from a single row.

To use a database control grid:

1.	Place a database control grid on a form.

2.	Set the grid's DataSource property to the name of a data source.

3.	Place individual data controls within the design cell for the grid. The design cell for the grid is the top or leftmost cell in the grid, and is the only cell into which you can place other controls.

4.	Set the DataField property for each data control to the name of a field. The data source for these data controls is already set to the data source of the database control grid.

5.	Arrange the controls within the cell as desired.

When you compile and run an application containing a database control grid, the arrangement of data controls you set in the design cell at run time is replicated in each cell of the grid. Each cell displays a different record in a dataset.

TDBCtrlGrid at design time

The following table summarizes some of the unique properties for database control grids that you can set at design time:

Property	Purpose
AllowDelete	True (default): Permits record deletion.False: Prevents record deletion.
AllowInsert	True (default): Permits record insertion.False: Prevents record insertion.
ColCount	Sets the number of columns in the grid. Default = 1.
Orientation	goVertical (default): Display records from top to bottom.goHorizontal: Displays records from left to right.
PanelHeight	Sets the height for an individual panel. Default = 72.
PanelWidth	Sets the width for an individual panel. Default = 200.
RowCount	Sets the number of panels to display. Default = 3.
ShowFocus	True (default): Displays a focus rectangle around the current record's panel at run time.False: Does not display a focus rectangle.


See also

TDBGrid

TDBCtrlGrid

Using data controls

Working with field components


Using decision support components

Section topics 	See also

The decision support components help you create cross-tabulated--or, crosstab--tables and graphs. You can then use these tables and graphs to view and summarize data from different perspectives. For more information on cross-tabulated data, see "About crosstabs."

The decision support components appear on the Decision Cube palette:

The decision cube, TDecisionCube, is a multidimensional data store.

The decision source, TDecisionSource, defines the current pivot state of a decision grid or a decision graph.

The decision query, TDecisionQuery, is a specialized form of TQuery used to define the data in a decision cube.

The decision pivot, TDecisionPivot, lets you open or close decision cube dimensions, or fields, by pressing buttons.

The decision grid, TDecisionGrid, displays single- and multidimensional data in table form.

The decision graph, TDecisionGraph, displays fields from a decision grid as a dynamic graph that changes when data dimensions are modified.

The following figure shows all the decision support components placed on a form at design time:


About crosstabs

Section topics 	See also

Cross-tabulations, or crosstabs, are a way of presenting subsets of data so that relationships and trends are more visible. Table fields become the dimensions of the crosstab while field values define categories and summaries within a dimension.

You can use the decision support components to set up crosstabs in forms. TDecisionGrid, shows data in a table, while TDecisionGraph, charts it graphically. TDecisionPivot has buttons that make it easier to display and hide dimensions and move them between columns and rows.

Crosstabs can be one-dimensional or multidimensional.

One-dimensional crosstabs

One-dimensional crosstabs show a summary row (or column) for the categories of a single dimension. For example, if Payment is the chosen column dimension and Amount Paid is the summary category, the crosstab in the following figure shows the amount paid for in each way:

Multidimensional crosstabs

Multidimensional crosstabs use additional dimensions for the rows and/or columns. For example, a two-dimensional crosstab could show amounts paid by payment method for each country.

A three-dimensional crosstab could show amounts paid by payment method and terms by country, as shown:


Guidelines for using decision support components

Section topics 	See also

The decision support components listed in "Using decision support components" can be used together to present multidimensional data as tables and graphs. More than one table or graph can be attached to each dataset. You can use more than one instance of TDecisionPivot to display the data from different perspectives at runtime.

To create a form with tables and graphs of multidimensional data:

1.	Create a form.

2.	Add these components to the form and use the Object Inspector to bind them as indicated:

A dataset, usually TDecisionQuery (for details, see "Creating decision datasets with TDecisionQuery") or TQuery

A decision cube, TDecisionCube, bound to the dataset by setting its DataSet property to the dataset's name

A decision source, TDecisionSource, bound to the decision cube by setting its DecisionCube property to the decision cube's name

3.	Add a decision pivot, TDecisionPivot, and bind it to the decision source with the Object Inspector by setting its DecisionSource property to the appropriate decision source name. The decision pivot is optional but useful; it lets the form developer and end users change the dimensions displayed in decision grids or decision graphs by pushing buttons.

In its default orientation, horizontal, buttons on the left side of the decision pivot apply to fields on the left side of the decision grid (rows); buttons on the right side apply to fields at the top of the decision grid (columns).

You can determine where the decision pivot's buttons appear by setting its GroupLayout property to xtVertical, xtLeftTop, or xtHorizontal (the default). For more information on decision pivot properties, see "Using decision pivots."

4.	Add one or more decision grids and graphs, bound to the decision source. For details, see "Creating and using decision grids" and "Creating and using decision graphs."

5.	Use the Decision Query editor or SQL property of TDecisionQuery (or TQuery) to specify the tables, fields, and summaries to display in the grid or graph. The last field of the SQL SELECT should be the summary field. The other fields in the SELECT must be GROUP BY fields. For instructions, see "Creating decision datasets with TDecisionQuery."

6.	Set the Active property of the decision query (or alternate dataset component) to True.

7.	Use the decision grid and graph to show and chart different data dimensions. See "Creating and using decision grids" and "Creating and using decision graphs" for instructions and suggestions.

For an illustration of all decision support components on a form, see the figure in "Using Decision Support components."


Using datasets with decision support components

Section topics 	See also

The only decision support component that binds directly to a dataset is the decision cube, TDecisionCube. TDecisionCube expects to receive data with groups and summaries defined by an SQL statement of an acceptable format. The GROUP BY phrase must contain the same non-summarized fields (and in the same order) as the SELECT phrase, and summary fields must be identified.

The decision query component, TDecisionQuery, is a specialized form of TQuery. This component can be used to more simply define the setup of dimensions (rows and columns) and summary values used to supply data to decision cubes, TDecisionCube. An ordinary TQuery or other dataset can also be used as a dataset for TDecisionCube, but the correct setup of the dataset and TDecisionCube are then the responsibility of the designer.

To work correctly with the decision cube, all projected fields in the dataset must either be dimensions or summaries. The summaries should be additive values (like sum or count), and should represent totals for each combination of dimension values. For maximum ease of setup, sums should be named "Sum..." in the dataset while counts should be named "Count..."

The Decision Cube can pivot, subtotal, and drill-in correctly only for summaries whose cells are additive. (SUM and COUNT are additive, while AVERAGE, MAX, and MIN are not)  Build pivoting crosstab displays only for grids that contain only additive aggregators.  If you are using non-additive aggregators, use a static decision grid that does not pivot, drill, or subtotal.

Since averages can be calculated using SUM divided by COUNT, a pivoting average is added automatically when SUM and COUNT dimensions for a field are included in the dataset.  Use this type of average in preference to an average calculated using an AVERAGE statement.

Averages can also be calculated using COUNT(*).  To use COUNT(*) to calculate averages, include a “COUNT(*) COUNTALL” selector in the query.  If you use COUNT(*) to calculate averages, the single aggregator can be used for all fields.  Use COUNT(*) in cases where none of the fields being summarized include blank values, or where a COUNT aggregator is not available for every field.


Creating decision datasets with TQuery or TTable

Section topics 	See also

If you use an ordinary TQuery component as a decision dataset, you must manually set up the SQL statement, taking care to supply a GROUP BY phrase which contains the same fields (and in the same order) as the SELECT phrase.

The SQL should look similar to this:

'SELECT ORDERS."Terms" , ORDERS."ShipVIA" , '

' ORDERS."PaymentMethod" , '

' sum( ORDERS."AmountPaid" )'

'FROM "ORDERS.DB" ORDERS'

'GROUP BY'

' ORDERS."Terms" , ORDERS."ShipVIA" ,

ORDERS."PaymentMethod"'

The ordering of the SELECT fields should match the ordering of the GROUP BY fields.

With TTable, you must supply information to the decision cube about which of the fields in the query are grouping fields, and which are summaries. To do this, Fill in the Dimension Type for each field in the DimensionMap of the Decision Cube. You must indicate whether each field is a dimension or a summary, and if a summary, you must provide the summary type. Since pivoting averages depend on SUM/COUNT calculations, you must also provide the base field name to allow the decision cube to match pairs of SUM and COUNT aggregators.


Creating decision datasets with TDecisionQuery

Section topics 	See also

All data used by the decision support components passes through the decision cube, which accepts a specially formatted dataset most easily produced by an SQL query. See "Using datasets with decision support components" for more information.

While both TTable and TQuery can be used as decision datasets, it is easier to use TDecisionQuery; the Decision Query editor supplied with it can be used to specify tables, fields, and summaries to appear in the decision cube and will help you set up the SELECT and GROUP BY portions of the SQL correctly.

To use the Decision Query editor:

1.	Select the decision query component on the form, then right-click and choose Decision Query Editor. The Decision Query Editor dialog box appears.

2.	For single-table queries, click the Select Table button.

For more complex queries involving multi-table joins, click the Query Builder button to display the Visual Query Builder.

3.	Choose the database to use.

4.	Choose the table or tables to use, and join them correctly by pointing to the common field in one table and dragging to the equivalent field in another table.

5.	Return to the Decision Query Editor dialog box.

6.	In the Decision Query Editor dialog box, select fields in the Available Fields list box and assign them to be either Dimensions or Summaries by clicking the appropriate right-arrow button. Note that average summaries are calculated from Sum and Count.

7.	By default, all fields and summaries defined in the SQL property of the decision query appear in the Active Dimensions and Active Summaries list boxes. To remove a dimension or summary, select it in the list and click the left-arrow beside the list, or double-click the item to remove. To add it back, select it in the Available Fields list box and click the appropriate right-arrow.

Once you define the contents of the decision cube, you can further manipulate dimension display with its DimensionMap property and the buttons of TDecisionPivot. For more information, see the topics, "Using decision cubes," "Using decision sources,", and "Using decision pivots."Note:	When you use the Decision Query editor, the query is initially handled in ANSI-92 SQL syntax, then translated (if necessary) into the dialect used by the server.  The Decision Query editor reads and displays only ANSI standard SQL.  The dialect translation is automatically assigned to the TDecisionQuery’s SQL property.  To modify a query, edit the ANSI-92 version in the Decision Query rather then the SQL property.


Decision query properties

Section topics 	See also

All decision query properties are inherited from other components. Important inherited properties are Active, described in online Help and the Visual Component Library Reference under TDataSet, and SQL, described under TQuery. Queries are described in more detail in "Working with queries."


Using decision cubes

Section topics 	See also

The decision cube component, TDecisionCube, is a multidimensional data store that fetches its data from a dataset (typically a specially structured SQL statement entered through TDecisionQuery or TQuery). The data is stored in a form that makes its easy to pivot (that is, change the way in which the data is organized and summarized) without needing to run the query a second time.


Decision cube properties and events

Section topics 	See also

The DimensionMap properties of TDecisionCube not only control which dimensions and summaries appear but also let you set date ranges and specify the maximum number of dimensions the decision cube may support. You can also indicate whether or not to display data during design. You can display names, (categories) values, subtotals, or data. Display of data at design time can be time consuming, depending on the data source.

When you click the field next to DimensionMap in the Object Inspector, the Decision Cube Editor dialog box appears. You can use its pages and controls to set the DimensionMap properties.

The OnRefresh event fires whenever the decision cube cache is rebuilt. Developers can access the new dimension map and change it at that time to free up memory, change the maximum summaries or dimensions, and so on. OnRefresh is also useful if users access the Decision Cube editor; application code can respond to user changes at that time.


Using the Decision Cube editor

Section topics 	See also

You can use the Decision Cube editor to set the DimensionMap properties of decision cubes. You can display the Decision Cube editor through the Object Inspector, as described in the previous section, or by right-clicking a decision cube on a form at design time and choosing Decision Cube Editor.

The Decision Cube Editor dialog box has two tabs:

Dimension Settings, used to activate or disable available dimensions, rename and reformat dimensions, put dimensions in a permanently drilled state, and set date ranges to display.

Memory Control, used to set the maximum number of dimensions and summaries that can be active at one time, to display information about memory usage, and to determine the names and data that appear during design time.

Viewing and changing dimension settings

To view the dimension settings, display the Decision Cube editor and click the Dimension Settings tab. Then, select a dimension or summary in the Available Fields list. Its information appears in the boxes on the right side of the editor:

To change the dimension or summary name that appears in the decision pivot, decision grid, or decision graph, enter a new name in the Display Name edit box.

To determine whether the selected field is a dimension or summary, read the text in the Type edit box. If the dataset is a TTable component, you can use Type to specify whether the selected field is a dimension or summary.

To disable or activate the selected dimension or summary, change the setting in the Active Type drop-down list box: Active, As Needed, or Inactive. Disabling a dimension or setting it to As Needed saves memory.

To change the format of that dimension or summary, enter a Delphi format string in the Format edit box.

To display that dimension or summary by Year, Quarter, or Month, change the setting in the Grouping drop-down list box. Note that you can choose Set in the Grouping combo box to put the selected dimension or summary in a permanently "drilled down" state. This can be useful for saving memory when a dimension has many values. For more information, see "Decision support components and memory control."

To determine the starting value for ranges, or the drill-down value for a “Set” dimension, first choose the appropriate Grouping value in the Grouping drop-down, and then enter the starting range value or permanent drill-down value in the Initial Value drop-down list..

Setting the maximum available dimensions and summaries

To determine the maximum number of dimensions and summaries available for decision pivots, decision grids, and decision graphs bound to the selected decision cube, display the Decision Cube editor and click the Memory Control tab. Use the edit controls to change the current settings, if necessary. These settings help to control the amount of memory required by the decision cube. For more information, see "Decision support components and memory control."

Viewing and changing design options

To determine how much information appears during design time, display the Decision Cube editor and click the Memory Control tab. Then, check the setting that indicates which names and data to display. Display of data or field names at design time can cause performance delays in some cases because of the time needed to fetch the data.


Using decision sources

Section topics 	See also

The decision source component, TDecisionSource, defines the current pivot state of decision grids or decision graphs. Any two objects which use the same decision source also share pivot states.


Properties and events

Section topics 	See also

The following are some special properties and events that control the appearance and behavior of decision sources:

The ControlType property of TDecisionSource, indicates whether the decision pivot buttons should act like check boxes (multiple selections) or radio buttons (mutually exclusive selections).

The SparseCols and SparseRows properties of TDecisionSource indicate whether to display columns or rows with no values; if True, sparse columns or rows are displayed.

TDecisionSource has the following events:

OnLayoutChange occurs when the user performs pivots or drill-downs that reorganize the data.

OnNewDimensions occurs when the data is completely altered, such as when the summary or dimension fields are altered.

OnSummaryChange occurs when the current summary is changed.

OnStateChange occurs when the Decision cube is activated or deactivated.

 	OnBeforePivot occurs when changes are committed but not yet reflected in the user interface. Developers have an opportunity to make changes, for example, in capacity or pivot state, before application users see the result of their previous action.

OnAfterPivot fires after a change in pivot state. Developers can capture information at that time.


Using decision pivots

Section topics 	See also

The decision pivot component, TDecisionPivot, lets you open or close decision cube dimensions, or fields, by pressing buttons. When a row or column is opened by pressing a TDecisionPivot button, the corresponding dimension appears on the TDecisionGrid or TDecisionGraph component. When a dimension is closed, its detailed data doesn't appear; it collapses into the totals of other dimensions. A dimension may also be in a "drilled" state, where only the summaries for a particular value of the dimension field appear.

You can also use the decision pivot to reorganize dimensions displayed in the decision grid and decision graph. Just drag a button to the row or column area or reorder buttons within the same area.

For illustrations of decision pivots at design time, see:
"Using Decision Support components,"FHXR33574
"Using crosstabs,"


Properties

Section topics 	See also

The following are some special properties that control the appearance and behavior of decision pivots:

The first properties listed for TDecisionPivot define its overall behavior and appearance. You might want to set ButtonAutoSize to False for TDecisionPivot to keep buttons from expanding and contracting as you adjust the size of the component.

The Groups property of TDecisionPivot defines which dimension buttons appear. You can display the row, column, and summary selection button groups in any combination. Note that if you want more flexibility over the placement of these groups, you can place one TDecisionPivot on your form which contains only rows in one location, and a second which contains only columns in another location.

Typically, TDecisionPivot is added above TDecisionGrid. In its default orientation, horizontal, buttons on the left side of TDecisionPivot apply to fields on the left side of TDecisionGrid (rows); buttons on the right side apply to fields at the top of TDecisionGrid (columns).

You can determine where TDecisionPivot's buttons appear by setting its GroupLayout property to xtVertical, xtLeftTop, or xtHorizontal (the default, described in the previous paragraph).


Creating and using decision grids

Section topics 	See also

Decision grid components, TDecisionGrid, present cross-tabulated data in table form. These tables are also called crosstabs, described in "About crosstabs." The figure in "Using Decision Support components" shows a decision grid on a form at design time.

  Creating decision grids

To create a form with one or more tables of cross-tabulated data:

1.	Follow steps 1 - 3 listed under "Guidelines for using decision support components."

2.	Add one or more decision grid components (TDecisionGrid) and bind them to the decision source, TDecisionSource, with the Object Inspector by setting their DecisionSource property to the appropriate decision source component.

3.	Continue with steps 5 - 7 listed under "Guidelines for using decision support components."

To add a graph to the form, follow the instructions in "Creating decision graphs."

 Using decision grids

The decision grid component, TDecisionGrid, displays data from decision cubes (TDecisionCube) bound to decision sources (TDecisionSource).

By default, the grid appears with dimension fields at its left side and/or top, depending on the grouping instructions defined in the dataset. Categories, one for each data value, appear under each field. You can

Open and close dimensions

Reorganize, or pivot, rows and columns

Drill down for detail

Limit dimension selection to a single dimension for each axis

For more information about special properties and events of the decision grid, see "Decision grid properties."

Opening and closing decision grid fields

If a plus sign (+) appears in a dimension or summary field, one or more fields to its right are closed (hidden). You can open additional fields and categories by clicking the sign. A minus sign (-) indicates a fully opened (expanded) field. When you click the sign, the field closes. This outlining feature can be disabled; see "Decision grid properties" for details.

Reorganizing rows and columns in decision grids

You can drag row and column headings to new locations within the same axis or to the other axis. In this way, you can reorganize the grid and see the data from new perspectives as the data groupings change. This pivoting feature can be disabled; see "Decision grid properties" for details.

If you included a decision pivot, you can push and drag its buttons to reorganize the display. See "Using decision pivots" for instructions.

Drilling down for detail in decision grids

You can drill down to see more detail in a dimension.

For example, if you right-click a category label (row heading) for a dimension with others collapsed beneath it, you can choose to drill down and see data only for that category. When a dimension is drilled, you do not see the category labels for that dimension displayed on the grid, because only the records for a single category value are being displayed. If you have a decision pivot on the form, it displays category values and lets you change to other values if you want.

To drill down into a dimension:

Right-click a category label and choose Drill In To This Value, or

Right-click a pivot button and choose Drilled In.

To make the complete dimension active again:

Right-click the corresponding pivot button, or

Right-click the decision grid in the upper-left corner and select the dimension.

Limiting dimension selection in decision grids

You can change the ControlType property of the decision source to determine whether more than one dimension can be selected for each axis of the grid. For more information, see "Using decision sources."


Decision grid properties

Section topics 	See also

The decision grid component, TDecisionGrid, displays data from the TDecisionCube component bound to TDecisionSource. By default, data appears in a grid with category fields on the left side and top of the grid.

The following are special properties that control the appearance and behavior of decision grids:

TDecisionGrid has unique properties for each dimension. To set these, choose Dimensions in the Object Inspector, then select a dimension. Its properties then appear in the Object Inspector: Alignment defines the alignment of category labels for that dimension, Caption can be used to override the default dimension name, Color defines the color of category labels, FieldName displays the name of the active dimension, Format can hold any standard Delphi format for that data type, and Subtotals indicates whether to display subtotals for that dimension. With summary fields, these same properties are used to changed the appearance of the data that appears in the summary area of the grid. When you're through setting dimension properties, either click a component in the form or choose a component in the drop-down list box at the top of the Object Inspector.

The Options property of TDecisionGrid lets you control display of grid lines (cgGridLines = True), enabling of outline features (collapse and expansion of dimensions with + and - indicators; cgOutliner = True), and enabling of drag-and-drop pivoting (cgPivotable = True).

The OnDecisionDrawCell event of TDecisionGrid gives you a chance to change the appearance of each cell as it is drawn. The event passes the String, Font, and Color of the current cell as var parameters. You are free to alter those var parameters to achieve effects such as special colors for negative values. In addition to the Drawstate which is passed by TCustomGrid, the event passes TDecisionDrawState, which can be used to determine what type of cell is being drawn. Further information about the cell can be fetched using the Cells, CellValueArray, or CellDrawState functions.

The OnDecisionExamineCell event of TDecisionGrid lets you hook the right-click-on-event to data cells, and is intended to allow a program to display information (such as detail records) about that particular data cell. When the user right-clicks a data cell, the event is supplied with all the information which is was used to compose the data value, including the currently active summary value and a ValueArray of all the dimension values which were used to create the summary value.


Creating and using decision graphs

Section topics 	See also

Decision graph components, TDecisionGraph, present cross-tabulated data in graphic form. Each decision graph shows the value of a single summary, such as Sum, Count, or Avg, charted for one or more dimensions.

Creating decision graphs

To create a form with one or more decision graphs:

1.	Follow steps 1 - 3 listed under "Guidelines for using decision support components."

2.	Add one or more decision graph components (TDecisionGraph) and bind them to the decision source, TDecisionSource, with the Object Inspector by setting their DecisionSource property to the appropriate decision source component.

3.	Continue with steps 5 - 7 listed under "Guidelines for using decision support components."

4.	Right-click the graph and choose Edit Chart to modify the appearance of the graph series. You can set template properties for each graph dimension, then set individual series properties to override these defaults. For details, see "Customizing decision graphs."

To add a decision grid--or crosstab table--to the form, follow the instructions in "Creating and using decision grids."

Using decision graphs

The decision graph component, TDecisionGraph, displays fields from the decision source (TDecisionSource) as a dynamic graph that changes when data dimensions are opened, closed, dragged and dropped, or rearranged with the decision pivot (TDecisionPivot).

Graphed data comes from a specially formatted dataset such as TDecisionQuery. For an overview of how the decision support components handle and arrange this data, see "Using decision support components."

By default, the first row dimension appears as the x-axis and the first column dimension appears as the y-axis.

You can use decision graphs instead of or in addition to decision grids, which present cross-tabulated data in table form. Decision grids and decision graphs that are bound to the same decision source present the same data dimensions. To show different summary data for the same dimensions, you can bind more than one decision graph to the same decision source. To show different dimensions, bind decision graphs to different decision sources.

For example, the first decision pivot and graph are bound to the first decision source and the second decision pivot and graph are bound to the second. So, each graph can show different dimensions.

Decision graphs bound to different decision sources:

For more information about what appears in a decision graph, see "The decision graph display."

For a discussion of decision graph properties and how to change the appearance and behavior of decision graphs, see "Customizing decision graphs."


The decision graph display

Section topics 	See also

By default, the decision graph plots summary values for categories in the first active row field (along the y-axis) against values in the first active column field (along the x-axis). Each graphed category appears as a separate series.

If only one dimension is selected--for example, by clicking only one TDecisionPivot button--only one series is graphed.

If you used a decision pivot, you can push its buttons to determine which decision cube fields (dimensions) are graphed. To exchange graph axes, drag the decision pivot dimension buttons from one side of the separator space to the other. If you have a one-dimensional graph with all buttons on one side of the separator space, you can use the Row or Column icon as a drop target for adding buttons to the other side of the separator and making the graph multidimensional.

If you want only one column and one row to be active at a time, you can set the ControlType property for TDecisionSource to xtRadio. Then, there can be only one active field at a time for each decision cube axis, and the decision pivot's functionality will correspond to the graph's behavior. xtRadioEx works the same as xtRadio, but does not allow the state where all row or all columns dimensions are closed.

When you have both a decision grid and graph connected to the same TDecisionSource, you'll probably want to set ControlType back to xtCheck to correspond to the more flexible behavior of TDecisionGrid.


Customizing decision graphs

Section topics 	See also

The decision graph component, TDecisionGraph, displays fields from the decision source (TDecisionSource) as a dynamic graph that changes when data dimensions are opened, closed, dragged and dropped, or rearranged with the decision pivot (TDecisionPivot). You can change the type, colors, marker types for line graphs, and many other properties of decision graphs.

To customize a graph:

1.	Right-click it and choose Edit Chart. The Chart Editing dialog box appears.

2.	Use the Chart page of the Chart Editing dialog box to view a list of visible series, select the series definition to use when two or more are available for the same series, change graph types for a template or series, and set overall graph properties.

The Series list on the Chart page shows all decision cube dimensions (preceded by Template:) and currently visible categories. Each category, or series, is a separate object. You can:

Add or delete series derived from existing decision-graph series. Derived series can provide annotations for existing series or represent values calculated from other series.

Change the default graph type, and change the title of templates and series.

For a description of the other Chart page tabs, search for the following topic in online Help: "Chart page (Chart Editing dialog box)."

3.	Use the Series page to establish dimension templates, then customize properties for each individual graph series.

By default, all series are graphed as bar graphs and up to 16 default colors are assigned. You can edit the template type and properties to create a new default. Then, as you pivot the decision source to different states, the template is used to dynamically create the series for each new state.

To customize individual series, follow the instructions in "Customizing decision graph series."

For a description of each Series page tab, search for the following topic in online Help: "Series page (Chart Editing dialog box)."

Setting decision graph template defaults

Decision graphs display the values from two dimensions of the decision cube: one dimension is displayed as an axis of the graph, and the other is used to create a set of series. The template for that dimension provides default properties for those series (such as whether the series are bar, line, area, and so on.) As users pivot from one state to another, any required series for the dimension are created using the series type and other defaults specified in the template.

A separate template is provided for cases where users pivot to a state where only one dimension is active. A one-dimensional state is often represented with a pie chart, so a separate template is provided for this case.

You can

Change the default graph type

Change other graph template properties

View and set overall graph properties

To change the default decision graph type:

1.	Select a template in the Series list on the Chart page of the Chart Editing dialog box.

2.	Click the Change button.

3.	Select a new type and close the Gallery dialog box.

To change color or other properties of a template:

1.	Select the Series page at the top of the Chart Editing dialog box.

2.	Choose a template in the drop-down list at the top of the page.

3.	Choose the appropriate property tab and select settings.

To view and set decision graph properties other than type and series:

1.	Select the Chart page at the top of the Chart Editing dialog box.

2.	Choose the appropriate property tab and select settings.

Customizing decision graph series

The templates supply many defaults for each decision cube dimension, such as graph type and how series are displayed. Other defaults, such as series color, are defined by TDecisionGraph. If you want you can override the defaults for each series.

The templates are intended for use when you want the program to create the series for categories as they are needed, and discard them when they are no longer needed. If you want, you can set up custom series for specific category values. To do this, pivot the graph so its current display has a series for the category you want to customize. When the series is displayed on the graph, you can use the Chart editor to:

Change the graph type.

Change other series properties.

Save specific graph series that you have customized.

To define series templates and set overall graph defaults, see the previous procedure in this topic, “Setting decision graph template defaults.”

Changing the series graph type

By default, each series has the same graph type, defined by the template for its dimension. To change all series to the same graph type, you can change the template type. See previous procedure in this topic, "To change the default decision graph type" for instructions.

To change the graph type for a single series:

1.	Select a series in the Series list on the Chart page of the Chart Editor.

2.	Click the Change button.

3.	Select a new type and close the Gallery dialog box.

4.	Check the Save Series check box.

Changing other decision graph series properties

To change color or other properties of a decision graph series:

1.	Select the Series page at the top of the Chart Editing dialog box.

2.	Choose a series in the drop-down list at the top of the page.

3.	Choose the appropriate property tab and select settings.

4.	Check the Save Series check box.

Saving decision graph series settings

By default, only settings for templates are saved at design time. Changes made to specific series are saved only if the Save box is checked for that series in the Chart Editing dialog box.

Saving series can be memory intensive, so if you don't need to save them you can uncheck the Save box.


Decision support components at runtime

Section topics 	See also

At runtime, users can perform many operations by left-clicking, right-clicking, and dragging visible decision components. These operations, discussed earlier in these topics, are summarized below.

 Decision pivots at runtime

Users can:

Left-click the summary button at the left end of the decision pivot to display a list of available summaries. They can use this list to change the summary data displayed in decision grids and decision graphs.

Right-click a dimension button and choose to:

Move it from the row area to the column area or the reverse

Drill In to display detail data

Left-click a dimension button following the Drill In command and choose:

Open Dimension to move back to the top level of that dimension

All Values to toggle between displaying just summaries and summaries plus all other values in decision grids

From a list of available categories for that dimension, a category to drill into for detail values

Left-click a dimension button to open or close that dimension

Drag and drop dimension buttons from the row area to the column area and the reverse; they can drop them next to existing buttons in that area or onto the row or column icon

 Decision grids at runtime

Users can:

Right-click within the decision grid and choose to:

Toggle subtotals on and off for individual data groups, for all values of a dimension, or for the whole grid

Display the Decision Cube editor, described on "Using the Decision Cube editor"

Toggle dimensions and summaries open and closed

Click + and - within the row and column headings to open and close dimensions

Drag and drop dimensions from rows to columns and the reverse.

 Decision graphs at runtime

Users can drag from side to side or up and down in the graph grid area to scroll through off-screen categories and values.


Decision support components and memory control

Section topics 	See also

When a dimension or summary is loaded into the decision cube, it takes up memory. Adding a new summary increases memory consumption linearly: that is, a decision cube with two summaries uses twice as much memory as the same cube with only one summary, a decision cube with three summaries uses three times as much memory as the same cube with one summary, and so on.

Memory consumption for dimensions increases more quickly. Adding a dimension with 10 values increases memory consumption ten times. Adding a dimension with 100 values increases memory consumption 100 times. Thus adding dimensions to a decision cube can have a dramatic effect on memory use, and can quickly lead to performance problems. This effect is especially pronounced when adding dimensions that have many values.

The decision support components have a number of settings to help you control how and when memory is used. For more information on the properties and techniques mentioned here, see TDecisionCube.


Setting maximum dimensions, summaries, and cells

Section topics 	See also

The decision cube's MaxDimensions and MaxSummaries properties can be used with the CubeDim.ActiveType property to control how many dimensions and summaries can be loaded at a time. You can set the maximum values on the Cube Capacity page of the Decision Cube editor to place some overall control on how many dimensions or summaries can be brought into memory at the same time.

Limiting the number of dimensions or summaries provides a rough limit on the amount of memory used by the decision cube. However, it does not distinguish between dimensions with many values and those with only a few. For greater control of the absolute memory demands of the decision cube, you can also limit the number of cells in the cube. Set the maximum number of cells on the Cube Capacity page of the Decision Cube editor.


Setting dimension state

Section topics 	See also

The ActiveType property controls which dimensions get loaded. You can set this property on the Dimension Settings tab of the Decision Cube editor using the Activity Type control. When this control is set to Active, the dimension is loaded unconditionally, and will always take up space.

Note that the number of dimensions in this state must always be less than MaxDimensions, and the number of summaries set to Active must be less than MaxSummaries. You should set a dimension or summary to Active only when it is critical that it be available at all times. An Active setting decreases the ability of the cube to manage the available memory.

When ActiveType is set to AsNeeded, a dimension or summary is loaded only if it can be loaded without exceeding the MaxDimensions, MaxSummaries or MaxCells limit. The decision cube will swap dimensions and summaries that are marked AsNeeded in and out of memory to keep within the limits imposed by MaxCells, MaxDimensions, and MaxSummaries. Thus, a dimension or summary may not be loaded in memory if it is not currently being used. Setting dimensions that are not used frequently to AsNeeded results in better loading and pivoting performance, although there will be a time delay to access dimensions which are not currently loaded.


Using paged dimensions

Section topics 	See also

When Binning is set to Set on the Dimension Settings tab of the Decision cube editor and Start Value is not null, the dimension is said to be "paged," or "permanently drilled down." You can access data for just a single value of that dimension at a time, although you can programmatically access a series of values sequentially. Such a dimension may not be pivoted or opened.

It is extremely memory intensive to include dimensional data for dimensions that have very large numbers of values. By making such dimensions paged, you can display summary information for one value at a time. Information is usually easier to read when displayed this way, and memory consumption is much easier to manage.


See also

TDecisionCube

TDecisionQuery

TDecisionSource

TDecisionPivot

TDecisionGraph

TDecisionGrid

TTable

Decision Cube components


Copying data and table structures

Section topics 	See also

These topics describe how to use the TBatchMove component in your database applications. TBatchMove encapsulates Borland Database Engine (BDE) features that enable you to duplicate a dataset, append records from one dataset to another, update records in one dataset with records from another dataset, and delete records from one dataset that match records in another dataset. TBatchMove is most often used to:

Download data from a server to a local data source for analysis or other operations.

Move a desktop database into tables on a remote server as part of an upsizing operation.

A batch move component can create tables on the destination that correspond to the source tables, automatically mapping the column names and data types as appropriate.


Creating a batch move component

Section topics 	See also

To create a batch move component:

1.	Place the table or query component for the dataset from which you want to import records (called the Source dataset) on a form or in a data module.

2.	Place the dataset component to which to move records (called the Destination dataset) on a form or in a data module.

3.	Place a TBatchMove component from the Data Access page of the Component palette in a data module or on a form, and set its Name property to a unique value appropriate to your application.

4.	Set the Source property of the batch move component to the name of the table to copy, append, or update from. You can select tables from the drop-down list of available dataset components.

5.	Set the Destination property to the dataset to create, append to, or update. You can select a destination table from the drop-down list of available dataset components, or add a new table component for the destination.

If you are appending, updating, or deleting, Destination must be an existing table.

If you are copying a table and Destination is an existing table, then executing the batch move overwrites all of the current data in the destination table.

If you are creating an entirely new table by copying an existing table, the resulting table has the name specified in the Name property of the table component to which you are copying. The resulting table type will be of a structure appropriate to the server specified by the DatabaseName property.

6.	Set the Mode property to indicate the type of operation to perform. Valid operations are batAppend (the default), batUpdate, batAppendUpdate, batCopy, and batDelete. For more information about these modes, see "Specifying a batch move mode."

7.	Optionally set the Transliterate property. If Transliterate is True (the default), character data is transliterated from the Source dataset's character set to the Destination dataset's character set as necessary.

8.	Optionally set column mappings using the Mappings property. You need not set this property if you want batch move to match column based on their position in the source and destination tables. For more information about mapping columns, see "Mapping data types."

9.	Optionally specify the ChangedTableName, KeyViolTableName, and ProblemTableName properties. Batch move stores problem records it encounters during the batch move operation in the table specified by ProblemTableName. If you are updating a Paradox table through a batch move, key violations can be reported in the table you specify in KeyViolTableName. ChangedTableName lists all records that changed in the destination table as a result of the batch move operation. If you do not specify these properties, these error tables are not created or used. For more information about handling batch move errors, see "Handling batch move errors."


Specifying a batch move mode

Section topics 	See also

The Mode property specifies the operation a batch move component performs:

Property	Purpose
batAppend	Append records to the destination table.
batUpdate	Update records in the destination table with matching records from the source table. Updating is based on the current index of the destination table.
batAppendUpdate	If a matching record exists in the destination table, update it. Otherwise, append records to the destination table.
batCopy	Create the destination table based on the structure of the source table. If the destination table already exists, it is dropped and recreated.
batDelete	Delete records in the destination table that match records in the source table.


Appending

Section topics 	See also

To append data, the destination dataset must already exist. During the append operation, the BDE converts data to appropriate data types and sizes for the destination dataset if necessary. If a conversion is not possible, an exception is raised and the data is not appended.


Updating

Section topics 	See also

To update data, the destination dataset must already exist and must have an index defined that enables records to be matched. If the primary index fields are used for matching, records with index fields in the destination dataset that match index fields records in the source dataset are overwritten with the source data. During the update operation, the BDE converts data to appropriate data types and sizes for the destination dataset if necessary.


Appending and updating

Section topics 	See also

To append and update data the destination dataset must already exist and must have an index defined that enables records to be matched. If the primary index fields are used for matching, records with index fields in the destination dataset that match index fields records in the source dataset are overwritten with the source data. Otherwise data from the source dataset is appended to the destination dataset. During append and update operations, the BDE converts data to appropriate data types and sizes for the destination dataset if necessary.


Copying

Section topics 	See also

To make a copy of a source dataset, the destination dataset should not already exist. If it does, the batch move operation overwrites the existing destination dataset with a copy of the source dataset.

If the source and destination datasets are maintained by different types of database engines, for example, Paradox and InterBase, the BDE creates a destination dataset with a structure as close as possible to that of the source dataset and automatically performs data type and size conversions as necessary.

Note:	TBatchMove does not copy metadata structures such as indexes, constraints, and stored procedures are not copied. You must recreate these metadata objects on your database server or through the SQL Explorer as appropriate.


Deleting

Section topics 	See also

To delete data in the destination dataset, it must already exist and must have an index defined that enables records to be matched. If the primary index fields are used for matching, records with index fields in the destination dataset that match index fields records in the source dataset are deleted in the destination table.


Mapping data types

Section topics 	See also

In batAppend mode, a batch move component creates the destination table based on the column data types of the source table. Columns and types are matched based on their position in the source and destination tables. That is, the first column in the source is matched with the first column in the destination, and so on.

To override the default column mappings, use the Mappings property. Mappings is a list of column mappings (one per line). This listing can take one of two forms. To map a column in the source table to a column of the same name in the destination table, you can use a simple listing that specifies the column name to match. For example, the following mapping specifies that a column named ColName in the source table should be mapped to a column of the same name in the destination table:

ColName

To map a column named SourceColName in the source table to a column named DestColName in the destination table, the syntax is as follows:

DestColName = SourceColName

If source and destination column data types are not the same, a batch move operation attempts a "best fit". It trims character data types, if necessary, and attempts to perform a limited amount of conversion, if possible. For example, mapping a CHAR(10) column to a CHAR(5) column will result in trimming the last five characters from the source column.

As an example of conversion, if a source column of character data type is mapped to a destination of integer type, the batch move operation converts a character value of '5' to the corresponding integer value. Values that cannot be converted generate errors. For more information about errors, see "Handling batch move errors."

When moving data between different table types, a batch move component translates data types as appropriate based on the dataset's server types. See the BDE online help file for the latest tables of mappings among server types.

Note:	To batch move data to an SQL server database, you must have that database server and Delphi Client/Server Suite with the appropriate SQL Link installed, or you can use ODBC if you have the proper third party ODBC drivers installed.


Executing a batch move

Section topics 	See also

Use the Execute method to execute a previously prepared batch operation at run time. For example, if BatchMoveAdd is the name of a batch move component, the following statement executes it:

BatchMoveAdd.Execute;

You can also execute a batch move at design time by right clicking the mouse on a batch move component and choosing Execute from the context menu.

The MoveCount property keeps track of the number of records that are moved when a batch move executes.

The RecordCount property specifies the maximum number of records to move. If RecordCount is zero, all records are moved, beginning with the first record in the source dataset. If RecordCount is a positive number, a maximum of RecordCount records are moved, beginning with the current record in the source dataset. If RecordCount is greater than the number of records between the current record in the source dataset and its last record, the batch move terminates when the end of the source dataset is reached. You can examine MoveCount to determine how many records were actually transferred.


Handling batch move errors

Section topics 	See also

There are two types of errors that can occur in a batch move operation: data type conversion errors and integrity violations. TBatchMove has a number of properties that report on and control error handling.

The AbortOnProblem property specifies whether to abort the operation when a data type conversion error occurs. If AbortOnProblem is True, then the batch move operation is canceled when an error occurs. If False, the operation continues. You can examine the table you specify in the ProblemTableName to determine which records caused problems.

The AbortOnKeyViol property indicates whether to abort the operation when a Paradox key violation occurs.

The ProblemCount property indicates the number of records that could not be handled in the destination table without a loss of data. If AbortOnProblem is True, this number is one, since the operation is aborted when an error occurs.

The following properties enable a batch move component to create additional tables that document the batch move operation:

ChangedTableName, if specified, creates a local Paradox table containing all records in the destination table that changed as a result of an update or delete operation.

KeyViolTableName, if specified, creates a local Paradox table containing all records from the source table that caused a key violation when working with a Paradox table. If AbortOnKeyViol is True, this table will contain at most one entry since the operation is aborted on the first problem encountered.

ProblemTableName, if specified, creates a local Paradox table containing all records that could not be posted in the destination table due to data type conversion errors. For example, the table could contain records from the source table whose data had to be trimmed to fit in the destination table. If AbortOnProblem is True, there is at most one record in this table since the operation is aborted on the first problem encountered.

Note:	If ProblemTableName is not specified, the data in the record is trimmed and placed in the destination table.


See also

TBatchMove


Creating a multi-tiered application

Section topics	See also

These topics describe how to use Delphi's new distributed dataset architecture to create a multi-tiered, client/server database application, both from scratch, and by converting an existing two-tiered client/server database application to a multi-tiered one.

A multi-tiered client/server application is a database application partitioned into logical units, each of which typically run in conjunction on separate machines and which share data and communicate with one another over a local-area network or even over the Internet. In its simplest model, sometimes called the "three-tiered model," an application is partitioned into thirds:

A client application, which provides a user interface on the user's machine.

An application server, which provides common data services to any number of client applications through distributed datasets, and which resides in a central networking location accessible to its clients.

A remote database server, which provides the relational database management system (RDBMS).

In this three-tiered model, the application server manages the exchange of data between clients and the remote database server, so it is sometimes called a "data broker." With Delphi you usually create only the application server and its clients, although, if you are really ambitious, you could create your own database back-end as well.

In more complex multi-tiered applications, there are additional services that reside between a client and a remote database server. For example, there might be a security services broker to handle secure Internet transactions, or bridge services to handle sharing of data with databases on platforms not directly supported by Delphi.

These topics focus on creating three-tiered database applications because Delphi provides new components that enable you both to separate client and application server database tasks and link them. Once you understand how to create and manage a three-tiered application, you also understand how to create and add additional service layers based on your needs.


A structural overview of a multi-tiered application

Section topics	See also

To the end user interacting with the client application, a multi-tiered database application looks and behaves no differently from a more traditional two-tiered application. User interaction takes place through standard Delphi data-aware controls associated with a client dataset component (TClientDataSet) through an intermediary data source component. For detailed information about using the properties, events, and methods of TClientDataSet, see "Creating and using a client dataset."

The client dataset is linked to a MIDAS connection object (TMIDASConnection), which, in turn, establishes the link to the application server. For more specific information about using the properties, events, and methods of TMIDASConnection, see "Establishing and maintaining a connection to an application server."

Client application architecture:

The MIDAS connection object on the client application establishes a link to the application server. The MIDAS connection receives an interface to a data provider on the application server. Delphi includes a TProvider component for application servers to encapsulate and create the provider interface. In turn, client datasets on the client use that data interface to request data directly from and send updates directly to the application server. The provider object is both an ActiveX CoClass that provides an interface to the client dataset and a data handler that:

Receives data requests from the client, retrieves the requested data from the database server, packages the data for transmission, and sends the data to the client dataset.

Receives updated data from the client dataset, resolves the updates, applying them to the database, and logging any updates that cannot be applied, and returns unresolved updates to the client for further reconciliation.

For detailed information about using the properties, events, and methods of TProvider, see "Creating a data provider for the application server."

Once a link between a client and the application server is established, the provider on the server and the client dataset on the client communicate through the provider's interface. The provider object establishes its connection to a database server through standard Delphi database and dataset objects (TDatabase, TSession, TSessionList, TQuery, TStoredProc, and TTable):

The following numbered steps illustrate a normal sequence of events for a multi-tiered client/server applications:

1.	A user starts the client application. The client connects to the application server (which can be specified at design time or run time). If the application server is not already running it starts. The client receives a provider interface from the application server.

2.	The client requests data from the application server. A client may request all data at once, or may request chunks of data throughout the session. For more information about retrieving data with a client dataset, see "Creating and using a client dataset."

3.	The application server retrieves the data from the database (first establishing a database connection, if necessary), packages it for the client, and returns a data packet to the client.

4.	The client decodes the data packet and displays the data to the user.

5.	The user updates records and inserts new ones. These modifications and additions are stored in a change log by the client.

6.	Eventually the client applies its updates to the application server, usually in response to a user action. To apply updates, the client packages its change log and sends it as data packet to the server.

7.	The application server decodes the package and posts updates and new records to the database server. If a record cannot be posted to the server (for example, because another user or application made changes to the record between the time the client requested it and the time the client applied its updates), the unposted record is saved by the application server. The process of posting records and caching problem records is called "resolving."

8.	When the application server finishes the resolving process, it returns any unposted records to the client for further resolution.

9.	The client reconciles unresolved records. There are many ways your client can reconcile unresolved records. Typically the client attempts to correct the situation that prevented records from being posted, or discards the changes. If the error situation can be rectified, the client later calls ApplyUpdates again to write the changes to the database.

10.	The client refreshes its data from the server.


Advantages of the multi-tiered database model

Section topics	See also

The multi-tiered database model breaks a database application into logical pieces. The client application can focus on data display and user interactions. The application server coordinates and processes requests for and updates to data from multiple clients. You can also use the application server to provide and enforce a standard set of business rules across all your client applications without having to encode them into each separate client.

There are three additional advantages to the multi-tiered model: a thinner client footprint, distributed data processing, and increased possibility for data security.

The size of client executables is small both because they share a data access DLL with other clients on the same machine, and because they use a distributed dataset based on repackaged data received from the application server. Because each piece of a multi-tiered application typically runs on a different machine from the other pieces, an application effectively shares each system's CPU for processing. Using a multi-tiered database model also provides an opportunity for increased data security because instead of every client communicating directly with your remote database server, the application server acts as an intermediary for all clients and reduces the number of entry points to your database server.


Overview of creating a multi-tiered application

Section topics	See also

In general, to create a multi-tiered database application:

1.	Create, run, and register the application server. Because the application server components supplied with Delphi are COM-enabled, you must register the application server as you do any ActiveX or OLE application. For more information about registering an application, see "Creating OLE Automation servers."

2.	Create a client application.

This order of creation is important. You should create and run the application server before you create a client. At design time, you should connect to the application server to test your client. You can, of course, create a client without specifying the application server at design time, and supply the server name only at run time, but doing so prevents you from seeing if your application works as expected when you code at design time.


Creating the application server

Section topics	See also

You create an application server very much as you create most Delphi database applications. The major difference is that the application server must include a provider interface of some type. You can either use a TProvider component, or the Provider property of a TDBDataSet descendant (such as TTable), to establish a dual interface for remote client interactions.

To create an application server, start a new project, save it, then:

1.	Add a new remote data module to the project, place the appropriate table, query, and stored procedure components on the data module and set them up to access the database server. Note that you cannot place TDatabase components on this data module because of name space conflicts. To use these components, place them in a separate data module and add that data module to the remote data module's uses clause.

Note:	A remote data module is different from a data module. It is an OLE Automation object that functions as a CoClass to establish a dual interface with a client application. To add a remote data module to a a project, choose File | New and choose Remote Data Module.

2.	Place a TProvider component or TDBDataSet descendant on the data module for each dataset to access.  You must explicitly export each provider in your remote data module so that it is also registered in the type library. To do this, select each provider component (either a TProvider or a TBDEDataSet), right-click, and in the popup menu select Export From <Name> in Data Module.

3.	If you use a provider component, set the DataSet property for the provider component to the name of the dataset to access. There are additional properties that you can set for the provider. For more detailed information about setting up a provider, see "Creating a data provider for the application server."

4.	Optionally write application server code to implement events, shared business rules, shared data validation, and shared security.

5.	Save, compile and, optionally, run the application server. While it is not an absolute requirement that the server be running in order to create and build a client application that uses it, you must run the application server once to register it as an OLE Automation object. Generally you will also want to set up and test your client connections at design time, so it to your advantage to run the server as you build your clients.

6.	If your server application will use TCP/IP sockets to communicate with client applications rather than (or in addition to) DCOM or OLEnterprise, run the application ScktSrvr.exe on the server machine.  ScktSrver.exe contains a TSocketDispatcher component that listens for client requests and transmits all use of the IProvider interface over a socket connection.


Creating the client application

Section topics	See also

In most regards, creating a multi-tiered client application is similar to creating a traditional two-tiered client. The major differences are that a multi-tiered client uses a:

TMIDASConnection component to establish a conduit to the application server.

One or more TClientDataSet components to link to a data provider on the application server. Data-aware controls on the client are connected through data source components to these client datasets instead of TTable, TQuery, and TStoredProc components.

To create a multi-tiered client application, start a new project and then:

1.	Add a new data module to the project.

2.	Place a TMIDASConnection component on the data module.

3.	Set the ConnectType property of the TMIDASConnection component to specify the type of connection you wish to use when communicating with the application server.

•	If you are using DCOM to communicate with the application server, set the ConnectType property to ctDCOM, and set the ServerName property to the name of the application server. The ServerName corresponds to the process ID for the server.

•	If you are using a TCP/IP socket to communicate with the application server, set the ConnectType property to ctSockets, set the ComputerName property to the IP address or host name of the application server, and set the ServerPort property to the value of the Port property on the socket dispatcher.

•	If you are using OLEnterprise to communicate with the application server, set the ConnectType property to ctOLEnterprise and set the ComputerName property to the name of the OLEnterprise server or the OLEnterprise Business Object Broker.

To learn more about setting up the MIDAS connection component, see "Establishing and maintaining a connection to an application server."

4.	Set the other MIDAS connection properties as needed for your application. For example, if the application server resides on another machine and you are using DCOM, then you must set the ComputerName property to identify the application server's machine name.  Or, if you are using an OLEnterprise Business Object Broker, you must set the UseBroker property to True.

5.	Place as many TClientDataSet components as needed on the data module, and set the RemoteServer property for each component to the name of the MIDAS connection component you placed in Step 2.

6.	Set the ProviderName property for each TClientDataSet component. If your MIDAS connection component is connected to the application server at design time, then you can choose available application server providers from the ProviderName property's drop-down list.

At this point, creating a client application is no different from creating any Delphi database application. The following sections discuss how a multi-tiered application updates records, and discusses how to handle master/detail updating at the client and application server levels.


Establishing and maintaining a connection to an application server

Section topics	See also

To establish and maintain a connection to an application server, a client application typically uses one or more TMIDASConnection components in a data module. You can find the TMIDASConnection component on the Data Access page of the Component palette.

The MIDAS connection component can:

Specify the type of communication protocol to use when communicating with the application server.

Specify the computer and server names for the application server.

Establish a connection to the application server.

Drop the connection.

Register client datasets to use its connection to the application server.

Change server connections at run time.

The MIDAS connection component is used to set up a connection to an application server. Usually the application server is on a different machine from the client application, but even if the server resides on the same machine as the client application (for example, during the building and testing of the entire multi-tier application), you can still use the MIDAS connection component to specify the communication protocol and to identify the server machine.


Specifying a connection using OLE or DCOM

Section topics	See also

If you know that all client machines are enabled to use OLE or DCOM, you can set the ConnectType property of the TMIDASConnection component to ctDCOM. When using OLE or DCOM to communicate with the application server, you must specify the application server’s location and name so that the TMIDASConnection component can locate the application server and establish the link.

Two properties of TMIDASConnection specify the server location and name:

ComputerName specifies the machine on which the server resides. By default ComputerName is blank.

ServerName specifies the name of the application server to which to connect. By default this property, too, is blank.

How you set the values for both these properties depends on your server's location and on the communication transport protocol your application uses.

In general, when ComputerName is blank, Delphi assumes that the application server resides on the client machine or, in the case of OLE and DCOM, that the application server has a system registry entry. If you do not provide a system registry entry for the application server on the client when using DCOM or OLE, and the server resides on a different machine from the client, you must supply ComputerName.

Note:	Even when using OLE and DCOM, you can specify ComputerName to override a system registry entry. This can be especially useful during development, testing, and debugging.

ServerName specifies the name of the application server to which the client application connects, or it specifies the GUID of the application server. If the application server has a system registry entry, you can select from available application servers at design time by choosing from the drop-down list for the ServerName property in the Object Inspector.

Note:	If you supply the name of a host computer or server that cannot be found, Delphi raises an exception.


Specifying a connection using sockets

Section topics	See also

You can establish a connection to the application server using sockets on any machine that has a TCP/IP address.  This method has the advantage of being applicable to more machines, but does not provide for using any security protocols.  To specify that the TMIDASConnection component should use sockets to communicate with the application server, set the ConnectType property to ctSockets.  You must then identify the IPAddress and Port of the socket on the application server that is listening for connection requests.

Two properties of TMIDASConnection specify the server location and port:

ComputerName specifies the IP Address or Host name of the server. You can enter either of these values to identify the server machine.

ServerPort specifies the port number of the socket dispatcher on the application server.  By default, ServerPort is 211, which is the default port number of the TSocketDispatcher component. If the application server uses a different port number, set the ServerPort property to match the Port property of the socket dispatcher used by the application server.  This Port property is set as a command line argument to the ScktSrvr.exe program that is run on the server machine, or it can be set explicitly if you have implemented your own form or data module that contains a socket dispatcher.


Specifying a connection using OLEnterprise

Section topics	See also

If the client machine has OLEnterprise installed, you can tell the TMIDASConnection component to use that when establishing a connection to the application server.  Set the ConnectType property to ctOLEnterprise. You can chose to use OLEnterprise to connect directly to the server machine, or you can use the OLEnterprise Business Object Broker.

To use OLEEnterprise without going through a Business Object Broker, set the UseBroker property to False and the ComputerName property to the name of the OLEnterprise server.

To use the OLEnterprise Business Object Broker, set the UseBroker property to True, and the ComputerName property to the name of the Business Object Broker.

For more information about using OLEnterprise, see the OLEnterprise documentation.


Establishing a connection to the server

Section topics	See also

At design and run times, you establish a connection to the application server by using a client application's MIDAS connection component and a client dataset component:

1.	Set the MIDAS Connection component's ConnectType property to indicate the protocol to use when establishing the connection to the application server.  Depending on the connection type, set the values of the ComputerName, ServerName, ServerPort, and UseBroker properties.

2.	Set a client dataset's RemoteServer property to the name of your MIDAS connection component.

3.	Set the client dataset's ProviderName property to the name of a provider on the application server. At design time you can choose from a drop-down list of available providers in the Object Inspector.

4.	Open the client dataset.

This sequence of events causes a call to the MIDAS connection component's GetProvider method. GetProvider adds the client dataset to its list of datasets, establishes the connection to the application server, and returns an IProvider interface that is passed to the client. The client dataset can communicate directly with the application server using this interface as long as the MIDAS connection component maintains the connection with the server.

Note:	The IProvider interface is actually a dual interface. For more information about interfaces and using them in Delphi applications, see "OLE and ActiveX Overview."

Before a MIDAS connection component establishes a connection to an application server, GetProvider calls the BeforeConnect event handler if it exists. If you want to perform any special actions prior to connecting, you must provide them in a BeforeConnect handler that you code. After establishing a connection, GetProvider also calls the AfterConnect event handler to perform any special actions you specify there.


Dropping a server connection

Section topics	See also

A MIDAS connection component drops its connection to the application server when:

The client application changes the value of the ServerName property.

The client application changes the value of the ComputerName property.

The client application changes the value of the ConnectType property.

The client application changes the value of the UseBroker property.

The client application sets the Connected property to False.

The client application frees the MIDAS connection component. A MIDAS connection component is automatically freed when a user closes the client application.

Changing ServerName or ComputerName at run time enables a client application to switch among available remote application servers. The MIDAS connection object drops the current connection, and establishes a new one.

Before a MIDAS connection component drops a connection, it automatically calls its BeforeDisconnect event handler, if one is provided. If you want to perform any special actions prior to disconnecting, you must provide them in an BeforeDisconnect handler. Similarly, after dropping the connection, the AfterDisconnect event handler is called. If you want to perform any special actions after disconnecting, you must provide them there.


Changing server connections at runtime

Section topics	See also

If you create several application servers, you can create client applications that can switch among them at run time using the MIDAS connection component's ServerName or ComputerName property. ServerName specifies the name of the application server to which a MIDAS connection component is connected.  The meaning of ComputerName depends on the value of the ConnectType property.

When you change the value of the ServerName or ComputerName property at run time, the MIDAS connection component drops the current connection, and establishes a new connection to the specified server.

Note:	Instead of using a MIDAS connection component to switch among available application servers, a client application can instead have more than one MIDAS connection component, each of which can be connected to different application servers.


Accessing data with client datasets

Section topics	See also

In a multi-tiered database application, you use a client application to access data with one or more TClientDataSet components. For a full introduction to client datasets, see "Creating and using a client dataset."

Client dataset components enable you to manage, view, and edit data. Client datasets in a client application usually get data from an application server. If you create a multi-tiered database application that also supports the briefcase model (enabling a user to download a copy of data, work on it off-line, and later upload changes to the application server) then your client datasets can also read data from and write data to a flat file. For more information about creating a client application that supports the briefcase model, see "Supporting the briefcase model."

Place as many client dataset components as necessary in each data module in your client application. Linking data-aware controls to client datasets through data source components is done just as it is for any TDataSet descendant. Linking a client dataset to a database server, however, differs from linking other datasets. A client dataset is not linked directly to an underlying database table. Instead, a client dataset is linked to a "data broker," or application server, that, in turn, maintains a link to the database server. For more information about linking a client dataset to an application server, see "Creating and using a client dataset."


Handling constraints

Section topics	See also

By default, a database server's constraints and default expressions are automatically imported by the SQL Explorer and replicated through attribute sets. As a result, server constraints and default expressions are passed to client datasets by the application server, where they can be imposed on user data editing. Using server constraints on client datasets ensures consistency across applications. For example, when constraints are in effect, user edits to data in a client application that would otherwise violate constraints are enforced on the client-side, and are never passed to the application server for eventual rejection by the database server. Bottom line: potentially fewer updates will generate error conditions during the updating process.

While importing of server constraints and expressions is an extremely valuable feature that enables a developer to preserve data integrity across platforms and applications, there may be times when an application needs to disable constraints on a temporary basis. For example, if a server constraint is based on the current maximum value in a field, but the client dataset fetches multiple packets of records, the current maximum value in a field on the client may differ from the maximum value on the database server, and constraints may be invoked differently. In another case, if a client application applies a filter to records when constraints are enabled, the filter may interfere in unintended ways with constraint conditions. In each of these cases, an application may disable constraints before requesting data.

To disable constraints temporarily, call a client dataset's DisableConstraints method. Each time DisableConstraints is called, a reference count is incremented. While the reference count is greater than zero, constraints are not enforced on the client dataset.

To reenable constraints for the client dataset, call the dataset's EnableConstraints method. Each call to EnableConstraints decrements the reference count. When the reference count is zero, constraints are enabled again.

Tip:	Always call DisableConstraints and EnableConstraints in paired blocks to ensure that constraints are enabled when you intend them to be.

For a general discussion of constraint and default expression handling in datasets, see "Working with field components" and "Understanding datasets."


Updating records

Section topics	See also

When a client application is connected to an application server, client datasets work with a local copy of data passed to them by the application server. The user sees and edits these copies in the client application's data-aware controls. If server constraints are enabled on the client dataset, a user's edits are constrained accordingly. User changes are temporarily stored by the client dataset in an internally maintained change log. The contents of the change log are stored as a data packet in the Delta property. To make the changes in Delta permanent, the client dataset must apply them to the database by calling ApplyUpdates.

Writing updates to the database is actually a four-step process:

1.	The client application calls the ApplyUpdates method belonging to a client dataset object. This method passes the contents of the client dataset's Delta property to the application server through the IProvider interface currently associated with the client dataset. Delta is a data packet that contains a client dataset's updated , inserted, and deleted records.

2.	The application server's TProvider object calls its own ApplyUpdates method to update the database. An update is an attempt to resolve each record in the cache received from the client with its corresponding record in the database. Resolution is a record-by-record process.

Record-level resolution is successful if the provider can write the updated or inserted record to the database, or remove a deleted record from the database.

Record-level resolution is unsuccessful if the provider cannot write the changed record to the database. Usually, resolution fails because the database record was changed by a different application subsequently to its retrieval by this client application, but prior to this client's request to apply updates. In this case, the client's version of the record is preserved by the provider so that it can be returned to the client application for reconciliation.

3.	The application server's TProvider object completes all record resolving and returns all unresolved records to the client for error reconciliation.

4.	The client application's reconciliation error handler attempts to correct error conditions on a record-by-records basis.

The application server performs the actual update operations on a record-by-record basis. The application server applies each record to the database. If an error occurs--for example, the database server rejects a change to a record because it invalidates a server constraint--the application server can attempt to resolve the error on its own. If it cannot resolve the error condition, it temporarily stores a copy of the offending record. When record processing is complete, the application server returns a Result data packet to the client dataset. The Result data packet contains all records that were not updated. It also contains error information, such as error messages and error codes.

A client dataset uses the data packet returned from the application server to reconcile errors in the OnReconcileError event handler. The OnReconcileError event handler is called once for each record in the data packet. For more information about reconciling errors in an OnReconcileError handler, see "Reconciling update errors."

Applying updates

Changes made to the client dataset's local copy of data are not sent to the application server until the client application calls the ApplyUpdates function for the dataset. ApplyUpdates takes a single parameter, MaxErrors, which indicates the maximum number of errors that the application server should tolerate before aborting the update process. ApplyUpdates returns the number of actual errors encountered, which is always less than or equal to MaxErrors plus one. This value is set to indicate the number of records it could not write to the database. The application server also returns those records to the client dataset in the dataset..

The client dataset is responsible for reconciling records that generate errors. ApplyUpdates actually calls the client dataset's own Reconcile method to send updates to the application server, and Reconcile, in turn, triggers the client dataset's OnReconcileError event handler, if it exists, once for each record that generated an error on the server. For more information about how to reconcile records that produce errors, see "Reconciling update errors."


Reconciling update errors

Section topics	See also

When ApplyUpdates executes, the records the client application's change log (Delta) are passed to the application server through the IProvider interface established between the client dataset and the application server's TProvider component. The provider returns error records and error information to the client dataset in a result data packet. ApplyUpdates then calls the client dataset's Reconcile method to handle any errors. If the application server returns an error count greater than zero, then for each record in the result data packet, Reconcile calls the client dataset's OnReconcileError event handler.

You should always code the OnReconcileError event handler, even if only to discard the records returned by the application server.

Caution:	Do not call any client dataset methods that change the current record (such as Next and Prior) in an OnReconcileError event handler. Doing so causes the event handler to enter an endless loop.

Here is the skeleton code for an OnReconcileError event handler:

procedure TForm1.ClientDataSetReconcileError(DataSet: TClientDataSet; E:EReconcileError;

 UpdateKind: TUpdateKind; var Action: TReconcileAction);

begin

 { Perform reconciliation here... }

end;

DataSet references the client dataset to which updates are applied. The following sections describe each of the other parameters passed to the reconcile error handler, and how those parameters are used.

Indicating the type of update that generated an error

UpdateKind is a parameter of type TUpdateKind. It describes the type of update that generated the error. Unless your error handler takes special actions based on the type of update being carried out, your code probably will not make use of this parameter.

The following table lists possible values for UpdateKind:

Value	Meaning
ukModify	Editing an existing record caused an error.
ukInsert	Inserting a new record caused an error.
ukDelete	Deleting an existing record caused an error.
Specifying the action to take

Action indicates the action to take when the OnReconcileError handler exits. On entry into the handler, Action is always set to the action taken by the resolution process on the server. The following table lists the possible values for Action and what they indicate:

Value	Meaning
raSkip	Skips updating the record that raised the error condition, and leaves the unapplied changes in the change log.
raAbort	Aborts the reconcile operation.
raMerge	Merges the updated record with the record on the server.
raCorrect	Reapplies the updated record to the database.
raCancel	Removes changes from the change log.
raRefresh	Updates the current record value and removes the changes from the change log.
Note:	The code in an OnReconcileError handler can examine and apply the OldValue, NewValue, and CurValue properties for each field in the current record, but should not call any methods that make a different record the current one.

If your error handler can correct the error condition that caused the handler to be invoked, set Action to the appropriate action to take on exit. For error conditions you correct, set Action to raMerge or raCorrect to apply the update for the record again.

When set to raSkip, the update for the row that caused the error is skipped, and the update for the record remains in the cache after all other updates are completed.

raAbort causes the entire update operation to end.

Working with error message text

The E parameter is of type EReconcileError. From this exception type you can extract an error message that you can display to users in your error handler. For example, the following code could be used to display the error message in the caption of a dialog box:

ErrorLabel.Caption := E.Message;

This parameter is also useful for determining the actual cause of the update error. You can extract specific error codes from EReconcileError, and take appropriate action based on it. For example, the following code checks to see if the update error is related to a key violation, and if it is, it sets the Action parameter to raSkip:

if E is EReconcileError then

 with EReconcileError(E) do

 begin

   if ErrorCode = DBIERR_KEYVIOL then

     Action := raSkip  { Key violation, just skip this record }

   else

     Action := raAbort { Don't know what's wrong, abort the update }

 end;


Using the IProvider interface

Section topics	See also

In addition to the properties and methods of the client dataset component, the IProvider interface allows the client application to interact with the Provider component in the application server. The Provider property of the client dataset provides access to the IProvider interface.

You can use the IProvider interface to

Obtain information about the remote data set.

Obtain records from the remote data set.

Pass parameters to the remote data set.

Apply updates from the client application to the remote database server.

Enable or disable constraint-checking.

Some of the properties and methods of the IProvider interface duplicate functionality that can be obtained directly from the client dataset. For example, you can apply updates from the client dataset without directly without using the ApplyUpdates method of the IProvider interface. In some cases, however, using the IProvider interface provides more control or allows you to perform actions that are not surfaced by the client dataset.

Obtaining information about the remote data set.

The IProvider interface allows you to fetch a description of the records in the remote dataset by calling the GetMetaData method. This returns a data packet that describes the structure of the records in the remote dataset, but contains no actual records.

Obtaining records from the remote data set

The IProvider interface provides a number of options for obtaining data from the application server. If there is no need to strictly control the number of records passed in a data packet, use the Data property to obtain all currently available records. The GetRecords method allows you to limit the number of records that are passed in the data packet.

Use the Reset method to obtain records from the beginning of the dataset rather than the set of records that follows the last data packet that was received. If the client dataset has already received metadata from the Provider data set, Reset should be called with the Metadata parameter set to False, so that only the data is sent. This data can then be added to the client dataset using the AppendData method.  If the client dataset is calling Reset before obtaining the first data packet from the Provider, the Metadata parameter should be set to True, so that the client dataset can determine how to interpret the data packet.

If you need more control over the fetching of data from the application server, you can supply an OnDataRequest event handler on the Provider component. The client dataset can then trigger that event handler by calling the DataRequest method of the IProvider interface. This provides a completely general mechanism by which the client dataset can request and receive information from the Provider.

Passing parameters to a remote dataset

When a dataset on an application server is a query or stored procedure, it may be necessary to provide parameter values to the remote data set. If the dataset on the application is a table, you may wish to provide parameters to specify a range. The IProvider interface allows you to send parameter values in a variant array by using the SetParams method. For example, to send two parameter values to the application server, the client application would write a line such as

ClientDataSet1.Provider.SetParams(VarArrayOf([1231, 'Unisco']);

The dataset on the application server would then assign 1231 to Params[0] and 'Unisco' to Params[1].

To assign parameters by name, rather than position, SetParams can take a variant array of variant arrays. Thus,

PData := VarArrayCreate([0,1], varVariant);

PData[0] := VarArrayOf(['CustNo',1231]);

PData[1] := VarArrayOf(['Company,'Unisco']);

ClientDataSet1.Provider.SetParams(PData);

would tell the Provider to set the value 1231 to the CustNo parameter and 'Unisco' to the Company paramter. This would be equivalent to writing

RemoteDataSet1.ParamByName('CustNo').Value := 1231;

RemoteDataSet1.ParamByName('Company').Value := 'Unisco'.

After the parameters are set, data is usually retrieved with a statement such as the following:

with ClientDataSet1 do

   AppendData(Provider.GetRecords(-1, RecCount), False);

Applying updates and checking constraints

The IProvider interface provides an ApplyUpdates method for updating records that does the same thing as the ApplyUpdates method of the client dataset. It also provides a boolean Constraints property that determines whether server constraints should be passed to the client dataset. Using the Constraints property of the IProvider interface is similar to using the EnableConstraints and DisableConstraints methods of the client dataset. The difference is that the EnableConstraints and DisableConstraints methods determine whether the client dataset should enforce the constraints, while the Constraints property determines whether the application server even sends the constraint information to the client dataset.

Note

For more information about specific properties or methods of the IProvider interface, see the corresponding properties or methods of the Provider (or Custom provider) component.


Creating a data provider for the application server

Section topics	See also

Each remote data module on an application server usually contains one or more TProvider components. A provider component establishes an IProvider interface between a TStoredProc, TQuery, or TTable component on the application server's remote data module, and a client dataset in a client application. The provider is the conduit between this dataset and a client dataset on a client application.

Note:	Instead of placing TProvider components in a remote data module and using them to establish IProvider interfaces that can be used with client datasets, you may choose to use the Provider property of TStoredProc, TQuery, or TTable to establish the interface, bypassing an explicit use of TProvider altogether. When you use a dataset component's Provider property directly, Delphi creates and manages an IProvider interface for you behind the scenes. What you sacrifice is control. Using TProvider gives your application more direct control over the provider interface.


Linking to a source dataset

Section topics	See also

To associate a provider component with a specific, BDE-enabled dataset component on the application server, set the DataSet property of the provider to the name of the dataset to use. At design time you can select from available datasets in the DataSet property drop-down list in the Object Inspector.

A client application can then use its MIDAS connection component to

1.	Establish a connection to the application server.

2.	Set the client dataset's ProviderName property to the provider component. (Setting this property allows the client application to link to the provider's dataset.)

3.	Retrieve data.

The client and the provider communicate directly through the IProvider interface.


Establishing a connection with a client application

Section topics	See also

When you set up and run an application server, it does not establish any connection with client applications. Instead, a client application uses its MIDAS connection component to establish a connection to the application server and to select a provider once the connection is established. In most cases, the provider selected is one of the application server's TProvider components.

When a provider is selected, it establishes a COM interface for communicating with the client. The COM interface is called IProvider. IProvider is derived from the IDispatch interface, and is implemented as a dual-interface between the application server and a specific client dataset. Each client connection establishes its own interface with the application server. For more information about COM, dual interfaces, and IDispatch, see "OLE and ActiveX Overview."

Once a connection is established, the interface between the application server and the client dataset is automatic and transparent to the developer and the user. Connection is maintained by client applications.


Handling client requests for data

Section topics	See also

In most multi-tiered applications, client requests for data are handled automatically. A client dataset specifies when it receives data according to the setting of its FetchOnDemand property. It specifies how much data it retrieves at a time with its PacketRecords property. The default settings for these properties guarantees that when a client dataset is opened, the application server sends it all available data immediately.

Even when a client application sets PacketRecords to receive data in multiple data packets, if FetchOnDemand is still True (the default), a client application automatically requests data from a provider on the application server data as needed. The provider always responds to these requests by sending more data if there is more data to send.

Note:	You can write an application server that calls the DataRequest method of the IProvider interface to customize a data request.


Handling update requests on the application server

Section topics	See also

A provider applies updates to database records based on a Delta data packet received from a client application. When a provider receives an update request, it calls its own ApplyUpdates method to write the records to the database.

The application server performs the actual update operations on a record-by-record basis. The application server applies each record to the database. If an error occurs--for example, the database server rejects a change to a record because it invalidates a server constraint--the application server calls its OnUpdateError event handler to resolve the error. If it cannot resolve the error condition, it temporarily stores a copy of the offending record. When record processing is complete, the application server returns a count of the errors it encountered to the client dataset, and copies the unresolved records into a results data packet that it passes back to the client for further reconciliation.

Note:	Ordinarily, update errors should not be processed on the application server, but passed back to the client application for resolution. One reason for this is because some errors may require user interaction to resolve, and user interaction takes place on the client application. Nevertheless, the following section describes the OnUpdateError handler for those developers who need to process errors at the application server level.

Resolving update errors on the provider

On the provider, the ApplyUpdates method attempts to resolve error conditions by calling the OnUpdateError event handler. In general it is better practice to handle errors on the client, where user interaction to correct error conditions is possible. In most cases, the OnUpdateError handler should merely pass the error-causing records and error information back to the client application.

procedure TForm1.Provider1UpdateError(DataSet: TClientDataSet, E: EUpdateError;

 UpdateKind: TUpdateKind; var Response: TResolverResponse);

begin

 { Perform error resolving here... }

end;

The following sections describe each of the parameters passed to the update error handler, and how those parameters are used.

Referencing the dataset to which to apply updates

DataSet references a temporary client dataset used to apply updates. You can use the NewValue and OldValue properties of each field to view and manipulate records associated with this dataset.

Indicating the type of update that generated an error

UpdateKind is a parameter of type TUpdateKind. It describes the type of update that generated the error. Unless your error handler takes special actions based on the type of update being carried out, your code probably will not make use of this parameter.

The following table lists possible values for UpdateKind:

Value	Meaning
ukModify	Updating an existing record caused an error.
ukInsert	Inserting a new record caused an error.
ukDelete	Deleting an existing record caused an error.
Specifying the action to take

Response is a parameter of type TResolverResponse. When the error handler is first called, the value for this parameter is always set to rrSkip. Unless you correct the error, you will not want to change this value. Response can be set to one of the following values:

Value	Meaning
rrSkip	Skips updating the record, records it, and the cause of the error condition in the result data packet to return to the client.
rrAbort	Aborts the update operation and passes the record and error conditions back to the client in a result data packet.
rrMerge	Merges the changed record with the record in the database.
rrApply	Applies a corrected update to the database.
rrIgnore	Ignores the record, and does not store it or its error information in the result data packet. Use this option with care because it effectively drops an error causing update without letting the client know an error occurred. Subsequently the client will silently remove the update from its change log as if the update had succeeded.

If your error handler can correct the error condition that caused the handler to be invoked, set Response to the appropriate action to take on exit. For error conditions you correct, set Response to rrMerge or rrApply to apply the update for the record again.

When set to rrSkip, the update for the row that caused the error is skipped, but the record and error information are added to the result data packet that is sent back to the client dataset when the provider completes updating the database.

rrAbort causes the entire update operation to end. No updates are applied to the database, and all records are returned to the client unchanged.

Working with error message text

The E parameter is of usually of type EUpdateError. From this exception type you can extract an error message that you can display to users in your error handler. For example, the following code could be used to display the error message in the caption of a dialog box:

ErrorLabel.Caption := E.Message;

This parameter is also useful for determining the actual cause of the update error. You can extract specific error codes from EUpdateError, and take appropriate action based on it. For example, the following code checks to see if the update error is related to a key violation, and if it is, it sets the Response parameter to rrSkip:

if E is EUpdateError then

 with EUpdateError(E) do

 begin

   if ErrorCode = DBIERR_KEYVIOL then

     Response := rrSkip  { Key violation, just skip this record }

   else

     Response := rrAbort { Don't know what's wrong, abort the update }

 end;


Handling server constraints

Section topics	See also

Most relational database management systems implement constraints on their tables to enforce data integrity. A constraint is a rule that governs data values in tables and columns, or that governs data relationships across columns in different tables. For example, most SQL-92 compliant relational databases support the following constraints:

NOT NULL, to guarantee that a value supplied to a column has a value.

NOT NULL UNIQUE, to guarantee that column value has a value and does not duplicate any other value already in that column for another record.

CHECK, to guarantee that a value supplied to a column falls within a certain range, or is one of a limited number of possible values.

CONSTRAINT, a table-wide check constraint that applies to multiple columns.

PRIMARY KEY, to designate one or more columns as the table's primary key for indexing purposes.

FOREIGN KEY, to designate one or more columns in a table that reference another table.

Note:	This list is not exclusive. Your database server may support some or all of these constraints in part or in whole, and may support additional constraints. For more information about supported constraints, see your server documentation.

Database server constraints obviously duplicate many kinds of data checks that traditional desktop database applications have managed in the past. You can take advantage of server constraints in your multi-tiered database applications without having to duplicate the constraints in application server or client application code.

The provider's Constraints property enables you to replicate and apply server constraints to data passed to and received from client applications. When Constraints is True (the default), your server's constraints are replicated to clients and affect client attempts to update data.

Note:	There may be times when you do not want to apply server constraints to data sent to a client application. For example, a client application that receives data in packets and permits local updating of records prior to fetching more records may need to disable some server constraints that might be triggered because of the temporarily incomplete set of data upon which updates are performed. To prevent constraint replication from the application server to a client dataset, set Constraints to False. Note that client datasets, too, can disable and enable constraints even if they are received from the application server. For more information about handling constraints on a client dataset, see "Creating and using a client dataset.".


Supporting the briefcase model

Section topics	See also

Most of these topics have described creating and using a client application in concert with an application server. In this model, a user starts the client application on one machine and connects over a network to an application server on a remote machine. The client requests data from the application server, and sends updates to it. The updates are applied by the application server to a database that is presumably shared with other clients throughout an organization.

Suppose, however, that your onsite company database contains valuable customer contact data that your sales representatives can use and update in the field. In this case, it would be useful if your sales reps could download some or all of the data from the company database, work with it on their laptops as they flew across the country, and even update records at existing or new customer sites. When the sales reps return onsite, they could upload their data changes to the company database for everyone to use. The ability to work with data off-line and then apply updates online at a later data is known as the "briefcase" model.

If you could use the briefcase model at your site, then you can take advantage of the client dataset component's ability to read and write data to flat files to create client applications that can be used both online with an application server, and off-line, as temporary one-tiered applications.

To create a client application that can be used with the application server and as a standalone, one-tiered application, you must add code to your client application that:

Permits a user to read data from an application server and save it to a local file.

Permits a user to load data from a local file and apply it to the application server.

Determines on start-up whether or not there is an application server to connect to, and establishes the correct IProvider interface for the situation. For example, if the client can connect to an application server, the IProvider interface is derived from the application server. Otherwise, the client application must create an IProvider behind the scenes (note that Delphi can create an IProvider as needed on the fly for you).

For more information about client dataset provider interfaces, and loading data from and writing data to a file with a client dataset, see "Creating and using a client dataset."


Modifying a two-tiered application to use the multi-tiered model

Section topics	See also

In a two-tiered Delphi client/server application, the application is a client that talks directly to a database server. Even so, the application can be thought of as having two parts: a database connection and a user interface. To make a two-tiered client/server application into a multi-tiered application you must:

Split your existing application into an application server that handles the database connection, and into a client application that contains the user interface.

Add an interface between the client and the application server.

There are a number of ways to proceed, but the following sequential steps may best keep your translation work to a minimum:

1. 	Create a new project for the application server, duplicate the relevant database connection portions of your former two-tiered application, and adapt it to use TProvider components to provide a data conduit between the application server and the client. For more information using a TProvider component, see "Creating a data provider for the application server."Copy your existing two-tiered project, remove its direct database connections, add a TMIDASConnection component to it, and substitute a TClientDataSet component for each TTable, TQuery, and TStoredProc component in the original project. Move the TTable, TQuery, and TStoredProc components to the application server's data modules, and set them up to use the TProvider interfaces you created for the application server in Step 1. For more information about linking a BDE-enabled dataset to a provider component, see "Linking to a source dataset." For more information about creating and using a MIDAS connection component, see "Establishing and maintaining a connection to an application server." For general information about using a client dataset component, see "Creating and using a client dataset."


Distributing a client applciation as an ActiveX control
Distributing a client application as an ActiveX control

Section topics	See also

Delphi’s distributed database architecture can be combined with its ActiveX features to distribute a client application as an ActiveX control.

You can create a client application that can be viewed from a Web browser as an Active form. To see the steps for creating a multi-tiered application see Overview of creating a multi-tiered application.  In addition to these steps, you must do the following:

Enabling the application server

Create and run your application server. See Creating the application server for detailed steps. After running the application server, the server is registered in the type library.

Creating an Active Form for the client application

Because the client application will be deployed as an ActiveX control, you must have a Web server that runs on the same system as the client application. You can use a ready-made Web server such as the Borland Web Server that ships with Borland’s Intrabuilder. Alternately, you can write your own Web server using Delphi’s socket components.  For more information about the socket components, see Working with sockets.

Create the client application normally, except start by choosing File | New | Active Form, rather than beginning the client project as an ordinary Delphi project.  For more information about creating client applications, see Creating the client application

If your client application uses a data module, add a call to explicitly create the data module in the active form initialization.

When your client application is finished, compile the project, and select Project | Web Deployment Options. In the Web Deployment Options dialog, you must do the following:

1. 	On the Project page, specify the Target directory, the URL for the target directory, and the HTML directory. Typically, the Target directory and the HTML directory will be the same as the projects directory for your Web Server. The target URL is typically the name of the server machine that is specified in the Windows Network|DNS settings.

2. 	On the Additional Files page, include dbclient.dll and stdvcl32.dll in your client application.

Finally, select Project| WebDeploy to deploy the client application as an active form.

Accessing the client application from a Web browser

Once you have built and deployed your client application, it can be accessed from an ActiveX-enabled Web browser on another machine.  For a Web browser to successfully launch your client application, the Web server must be running on the machine that has the client application.

If the client application uses DCOM to communicate between the client application and the application server, the machine with the Web browser must be enabled to work with DCOM.  If the machine with the Web browser is a Windows 95 machine, it must have installed DCOM95, which is available from Microsoft.  If the client application uses OLEnterprise to communicate with the appication server, the machine with the Web browser must have OLEnterprise installed. If the client application uses sockets to communicate between the client application and the application server, there are no special restrictions on the machine with the Web browser

Any Web browser that can run Active forms can run your client application by specifying the .HTM file that was created when you deployed the client application.  This .HTM file has the same name as your client application project, and appears in the directory specified as the Target directory.


See also

TProvider

TDatabase

TRemoteServer

TDataSet

TClientDataSet

TDBDataSet

TTable

TQuery

TSession

TSessionList

TStoredProc

Data Access components


Overview of component creation

Section topics 	See also

This set of topics provides an overview of component design and the process of writing components for Delphi applications. The material here assumes that you have some familiarity with Delphi and its standard components.

The Visual Component Library

Components and classes

How do you create components?

What goes in a component?

Creating a new component

Testing uninstalled components


The Visual Component Library

Section topics 	See also

Delphi's components are all part of a class hierarchy called the Visual Component Library (VCL). The following figure shows the relationship of the classes that make up the VCL.

The class TComponent is the shared ancestor of every component in the VCL. TComponent provides the minimal properties and events necessary for a component to work in Delphi. The various branches of the library provide other, more specialized capabilities.

When you create a component, you add to the VCL by deriving a new class from one of the existing class types in the hierarchy.


Components and classes

Section topics 	See also

Because components are classes, component writers work with objects at a different level from application developers. Creating new components requires that you derive new classes.

Briefly, there are two main differences between creating components and using them in applications. When creating components,

You have access to parts of the class that are inaccessible to application programmers.

You add new parts (such as properties) to your components.

Because of these differences, you need to be aware of more conventions and to think about how application developers will use the components you write.


How do you create components?

Section topics 	See also

A component can be almost any program element that you want to manipulate at design time. Creating a component means deriving a new class from an existing one. You can derive a new component from any existing component, but the following are the most common ways to create components:

Modifying existing controls

Creating windowed controls

Creating graphic controls

Subclassing Windows controls

Creating nonvisual components

The following table summarizes the different kinds of components and the classes you use as starting points for each.

To do this	Start with this type
Modify an existing component	any existing component, such as TButton or TListBox, or an abstract component type, such as TCustomListBox
Create an original control	TWinControl
Create a graphic control	TGraphicControl
Subclassing a Windows control	any Windows control
Create a nonvisual component	TComponent

You can also derive classes that are not components and cannot be manipulated on a form. Delphi includes many such classes, like TIniFile and TFont.


Modifying existing controls

Section topics 	See also

The simplest way to create a component is to customize an existing one. You can derive a new component from any of the components provided with Delphi.

Some controls, such as list boxes and grids, come in several variations on a basic theme. In these cases, the VCL includes an abstract class (with the word "custom" in its name, such as TCustomGrid) from which to derive customized versions.

For example, you might want to create a special kind of list box that does not have some of the properties of the standard TListBox class. You can't remove a property from an ancestor class, so you need to derive your component from something above TListBox in the hierarchy. Rather than force you to start from the abstract TWinControl class and reinvent all the list box functions, the VCL provides TCustomListBox, which implements the properties of a list box but does not publish all of them. When you derive a component from an abstract class like TCustomListBox, you publish only the properties you want to make available in your component and leave the rest protected.


Creating windowed controls

Section topics 	See also

Windowed controls are objects that appear at runtime and that the user can interact with. Each windowed control has a window handle, accessed through its Handle property, that lets Windows identify and operate on the control. The handle allows the control to receive input focus and can be passed to Windows API functions.

All windowed controls descend from the TWinControl class. These include most standard Windows controls, such as push buttons, list boxes, and edit boxes. While you could derive an original control (one that's not related to any existing control) directly from TWinControl, Delphi provides the TCustomControl component for this purpose. TCustomControl is a specialized windowed control that makes it easier to draw complex visual images.


Creating graphic controls

Section topics 	See also

If your control doesn't need to receive input focus, you can make it a graphic control. Graphic controls are similar to windowed controls, but have no window handles, and therefore consume fewer system resources. Components like TLabel, which never receive input focus, are graphic controls.

Delphi supports the creation of custom controls through the TGraphicControl component. TGraphicControl is an abstract class derived from TControl. Although you can derive controls directly from TControl, it is better to start from TGraphicControl, which provides a canvas to paint on and handles WM_PAINT messages; all you need to do is override the Paint method.


Subclassing Windows controls

Section topics 	See also

In traditional Windows programming, you create custom controls by defining a new window class and registering it with Windows. The window class (which is similar to the objects or classes of object-oriented programming) contains information shared among instances of the same sort of control; you can base a new window class on an existing class, which is called subclassing. You then put your control in a dynamic-link library (DLL), much like the standard Windows controls, and provide an interface to it.

Using Delphi, you can create a component "wrapper" around any existing window class. So if you already have a library of custom controls that you want to use in Delphi applications, you can create Delphi components that behave like your controls, and derive new controls from them just as you would with any other component.

For examples of the techniques used in subclassing Windows controls, see the components in the STDCTLS unit that represent standard Windows controls, such as TEdit.


Creating nonvisual components

Section topics 	See also

Nonvisual components are used as interfaces for elements like databases (TDataSet) and system clocks (TTimer), and as placeholders for dialog boxes (TCommonDialog and its descendants). Most of the components you write are likely to be visual controls. Nonvisual components can be derived directly from TComponent, the abstract base class for all components.


What goes in a component?

Section topics 	See also

To make your components reliable parts of the Delphi environment, you need to follow certain conventions in their design. This section discusses the following topics:

Removing dependencies

Properties, events, and methods

Graphics encapsulation

Registration


Removing dependencies

Section topics 	See also

One quality that makes components more usable is the absence of restrictions on what they can do at any point in their code. By their nature, components are incorporated into applications in varying combinations, orders, and contexts. You should design components that function in any situation, without preconditions.

An excellent example of removing dependencies is the Handle property of TWinControl. If you've written Windows applications before, you know that one of the most difficult and error-prone aspects of getting a program running is making sure that you don't try to access a window or control until you've created it by calling the CreateWindow API function. Delphi windowed controls relieve users from this concern by ensuring that a valid window handle is always available when needed. By using a property to represent the window handle, the control can check whether the window has been created; if the handle isn't valid, the control creates a window and returns the handle. Thus, whenever an application's code accesses the Handle property, it is assured of getting a valid handle.

By removing background tasks like creating the window, Delphi components allow developers to focus on what they really want to do. Before passing a window handle to an API function, there is no need to verify that the handle exists or to create the window. The application developer can assume that things will work, instead of constantly checking for things that might go wrong.

Although it can take time to create components that are free of dependencies, it is generally time well spent. It not only spares application developers from repeated and drudgery, but it reduces your documentation and support burdens.


Properties, events, and methods

Section topics 	See also

Aside from the visible image manipulated in the Form designer, the most obvious attributes of a component are its properties, events, and methods. Each of these has  a section devoted to it in this file, but the discussion below explains some of the motivation for their use.

Properties

Properties give the application developer the illusion of setting or reading the value of a variable, while allowing the component writer to hide the underlying data structure or to implement special processing when the value is accessed.

There are several advantages to using properties:

Properties are available at design time. The application developer can set or change initial values of properties without having to write code.

Properties can check values or formats as the application developer assigns them. Validating input at design time prevents errors.

The component can construct appropriate values on demand. Perhaps the most common type of error programmers make is to reference a variable that hasn't been declared or initialized. By representing data with a property, you can ensure that a value is always assigned.

Properties allow you to hide data under a simple, consistent interface. You can alter the way information is structured in a property without making the change visible to application developers.

"Creating properties" explains how to add properties to your components.

Events

An event is a special property whose value changes in response to input at runtime. Events give the application developer hooks for attaching specific blocks of code to specific runtime occurrences, such as mouse actions and keystrokes. The code that executes when an event occurs is called an event handler.

Events allow application developers to specify responses to different kinds of input without defining new components.

"Creating events" explains how to implement standard events and how to define new ones.

Methods

Class methods are procedures and functions that operate on a class rather than on specific instances of the class. For example, every component's constructor method (Create) is a class method. Component methods are procedures and functions that operate on the component instances themselves. Application developers use methods to direct a component to perform a specific action or return a value not contained by any property.

Because they require execution of code, methods can be called only at runtime. Methods are useful for several reasons:

Methods encapsulate the functionality of a component in the same object where the data resides.

Methods can hide complicated procedures under a simple, consistent interface. An application developer can call a component's AlignControls method without knowing how the method works or how it differs from the AlignControls method in another component.

Methods allow updating of several properties with a single call.

"Creating methods" explains how to add methods to your components.


Graphics encapsulation

Section topics 	See also

Delphi simplifies Windows graphics by encapsulating the various graphic tools into a canvas. The canvas represents the drawing surface of a window or control and contains other classes, such as a pen, a brush, and a font. A canvas is much like a Windows device context, but it takes care of all the bookkeeping for you.

If you've written a graphical Windows application, you're familiar with the requirements imposed by Windows' graphics device interface (GDI). For example, GDI limits on the number of device contexts available and makes you restore graphic objects to their initial state before destroying them.

With Delphi, you don't have to worry about these things. To draw on a form or other component, you access the component's Canvas property. If you want to customize a pen or brush, you set its color or style. When you finish, Delphi disposes of the resources. Delphi also caches resources to avoid recreating them if your application frequently uses the same kinds of resource.

You still have full access to the Windows GDI, but you'll often find that your code is simpler and runs faster if you use the canvas built into Delphi components. Graphics features are detailed in"Using graphics in components".


Registration Overview
Registration

Section topics 	See also

Before you can install your components in the Delphi IDE, you have to register them. Registration tells Delphi where you want your component to appear on the Component palette. You can also customize the way Delphi stores your components in the form file. Registration is explained in Chapter 26, Registering components.


Creating a new component

Section topics 	See also

To create a component:

1.	Create a unit for the new component.

2.	Derive your component from an existing component type.

3.	Add properties, methods, and events.

4.	Register your component with Delphi.

5.	Create a Help file for your component and its properties, methods, and events.

6.	Create a package (a special dynamic-link library) so that you can install your component in the Delphi IDE.

When you finish, the complete component includes these files:

A package (.DPL) or package collection (.DPC) file.

A compiled package (.DCP) file.

A compiled unit (.DCU) file.

A palette bitmap (.DCR) file.

A Help (.HLP) file.

A Help keyword (.KWF) file.

The Help files are optional, but can make your components easier to use.

There are several basic steps that you perform whenever you create a new component. These steps are outlined below; other examples in this document assume that you know how to perform them.

You can create a new component two ways:

Using the Component wizard

Creating a component manually

You can use either of these methods to create a minimally functional component ready to install on the Component palette.After installing, you can add your new component to a form and test it at both design time and runtime. You can then add more features to the component, update the Component palette, and continue testing.


Using the Component wizard

Section topics 	See also

The Component wizard simplifies the initial stages of creating a component. When you use the Component wizard, you need to specify only these things:

The class from which it is derived.

The class name for the new component.

The Component palette page you want it to appear on.

The name of the unit in which the component is created.

The search path where the unit is found.

The name of the package in which you want to place the component.

The Component wizard performs the same tasks you would when creating a component manually:

Creating a unit.

Deriving the component.

Registering the component.

The Component wizard cannot add components to an existing unit. You must add components to existing units manually.

To open the Component wizard, choose one of these two methods:

Choose Component|New Component.

Choose File|New and double-click on Component

Fill in the fields in the Component wizard:

1.	In the Ancestor Type field, specify the class from which you are deriving your new component.

2.	In the Class Name field, specify the name of your new component class.

3.	In the Palette Page field, specify the page on the Component palette on which you want the new component to be installed.

4.	Specify the name of the unit in which you want the component class declared.

5.	If the unit is not on the search path, edit the search path in the Search Path field as necessary.

To place the component in a new or existing package, click Install and use the dialog box that appears to specify a package. This will open the Package editor (which is described in the User's Guide) and display a dialog that prompts you to automatically compile the package and install your new component on the palette.

Warning:	If you derive a component from a VCL class whose name begins with "custom" (such as TCustomControl), do not try to place the new component on a form until you have overriden any abstract methods in the original component. Delphi cannot create instance objects of a class that has abstract properties or methods.

To see the source code for your unit, click View Unit. (If the Component wizard is already closed, open the unit file in the Code editor by selecting File|Open.) Delphi creates a new unit containing the class declaration and the Register procedure, and adds a uses clause that includes all the standard Delphi units. The unit looks like this:

unit MyControl;

interface

uses

 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type

 TMyControl = class(TCustomControl)

 private

   { Private declarations }

 protected

   { Protected declarations }

 public

   { Public declarations }

 published

   { Published declarations }

 end;

procedure Register;

implementation

procedure Register;

begin

 RegisterComponents('Samples', [TMyControl]);

end;

end.


Creating a component manually

Section topics 	See also

The easiest way to create a new component is to use the Component wizard. You can, however, perform the same steps manually.

To create a component manually:

1.	Create a unit file.

2.	Derive the component.

3.	Register the component.


Creating a unit file

Section topics 	See also

A unit is a separately compiled module of Object Pascal code. Delphi uses units for several purposes. Every form has its own unit, and most components (or groups of related components) have their own units as well

When you create a component, you either create a new unit for the component or add the new component to an existing unit.

To create a unit, choose File|New to and double-click on Unit. Delphi creates a new unit file and opens it in the Code editor.

To open an existing unit, choose File|Open and select the source code unit that you want to add your component to.

Note:	When adding a component to an existing unit, make sure that unit already contains only component code. Adding component code to a unit that contains, for example, a form, causes errors in the Component palette.

Once you have either a new or existing unit for your component, you can derive the component class.


Deriving the component

Section topics 	See also	Example

Every component is a class derived from TComponent, from one of its more specialized descendants (such as TControl or TGraphicControl), or from an existing component class. describes which class to derive from for different kinds of components.

To derive a component, add an object type declaration to the interface part of the unit that will contain the component.


An example of deriving a component

Section topics 	See also

To create a simple component class, a nonvisual component descended directly from TComponent, add the following class declaration to the interface part of your component unit:

type
  TNewComponent = class(TComponent)
  end;

So far the new component does nothing different from TComponent. You've created a framework on which you'll build your new component.


Registering the component

Section topics 	See also	Example

Registration is a simple process that tells Delphi which components to add to its component library, and on which pages of the Component palette they should appear. "Registering components" describes the registration process in more detail.

To register a component:

1.	Add a procedure named Register to the interface part of the component's unit. Register takes no parameters, so the declaration is very simple:

procedure Register;

If you're adding a component to a unit that already contains components, it should already have a Register procedure declared, so you don't need to change the declaration.

2.	Write the Register procedure in the implementation part of the unit, calling RegisterComponents for each component you want to register. RegisterComponents is a procedure that takes two parameters: the name of a Component palette page and a set of component types. If you're adding a component to an existing registration, you can either add the new component to the set in the existing statement, or add a new statement that calls RegisterComponents.


An example of registering a component

Section topics 	See also

To register a component named TMyControl and place it on the Samples page of the palette, you would add the following Register procedure to the unit that contains TMyControl's declaration:

procedure Register;
begin
  RegisterComponents('Samples', [TNewControl]);
end;

This Register procedure places TMyControl on the Samples page of the Component palette.

Once you register a component, you can compile it into a package (see "Creating packages") and install it on the Component palette.


Testing uninstalled components

Section topics 	See also	Example

You can test the runtime behavior of a component before you install it on the Component palette. This is particularly useful for debugging newly-created components, but the same technique works with any component, whether or not it is on the Component palette.

You test an uninstalled component by emulating the actions performed by Delphi when the component is selected from the palette and placed on a form.

To test an uninstalled component:

1.	Add the name of component's unit to the form unit's uses clause.

2.	Add an object field to the form to represent the component.

This is one of the main differences between the way you add components and the way Delphi does it. You add the object field to the public part at the bottom of the form's type declaration. Delphi would add it above, in the part of the type declaration that it manages.

Never add fields to the Delphi-managed part of the form's type declaration. The items in that part of the type declaration correspond to the items stored in the form file. Adding the names of components that do not exist on the form can render your form file invalid.

3.	Attach a handler to the form's OnCreate event.

4.	Construct the component in the form's OnCreate handler.

When you call the component's constructor, you must pass a parameter specifying the owner of the component (the component responsible for destroying the component when the time comes). You will nearly always pass Self as the owner. In a method, Self is a reference to the object that contains the method. In this case, in the form's OnCreate handler, Self refers to the form.

5.	Assign the Parent property.

Setting the Parent property is always the first thing to do after constructing a control. The parent is the component that contains the control visually; usually it is the form on which the control appears, but it might be a group box or panel. Normally, you'll set Parent to Self, that is, the form. Always set Parent before setting other properties of the control.

Warning:	If your component is not a control (that is, if TControl is not one of its ancestors), skip this step. If you accidentally set the form's Parent property (instead of the component's) to Self, you can cause an operating-system problem.

6.	Set any other component properties as desired.


An example of testing uninstalled components

Section topics 	See also

Suppose you want to test a new component of type TMyControl in a unit named MyControl. Create a new project, then follow the steps to end up with a form unit that looks like this:

unit Unit1;
interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, MyControl;	{ 1. Add NewTest to uses clause }

type
  TForm1 = class(TForm)

   procedure FormCreate(Sender: TObject);	{ 3. Attach a handler to OnCreate }

 private
    { Private declarations }

 public
    { Public Declarations }

   MyControl1: TMyControl1;	{ 2. Add an object field }

 end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);

begin
  MyControl1 := TMyControl.Create(Self);	{ 4. Construct the component }

 MyControl1.Parent := Self;	{ 5. Set Parent property if component is a control }

 MyControl1.Left := 12;	{ 6. Set other properties... )
  ...	{ ...continue as needed }

end;

end.


See also

TCustomControl

TCustomProvider

TCustomImageList

TCustomContentProvider

TCustomRemoteServer

TCustomSocket

TCustomLabel

TCustomComboBox

TCustomEdit

TCustomHotKey

TCustomListBox

TCustomListView

TCustomStaticText

TCustomTabControl

TCustomTreeView

TCustomUpDown

TCustomForm

TCustomCheckBox

TCustomGrid

TCustomGroupBox

TCustomPanel

TCustomDBGrid

TCustomOutline

TCustomRadioGroup

TCustomMaskEdit

TCustomMemo

TCustomRichEdit

TCustomMemoryStream

TWinControl

TCustomControl

TCustomControl

TDataSet

TComponent

TTimer

TCommonDialog


Object-oriented programming for component writers

Section topics	See also

If you've written applications with Delphi, you know that a class contains both data and code, and that you can manipulate classes at design time and at runtime. In that sense, you've become a component user.

When you create new components, you deal with classes in ways that application developers never need to. You also try to hide the inner workings of the component from the developers who will use it. By choosing appropriate ancestors for your components, designing interfaces that expose only the properties and methods that developers need, and following the other guidelines in these topics, you can create versatile, reusable components.

Before you start creating components, you should be familiar with these topics, which are related to object-oriented programming (OOP):

Creating new classes

Ancestors, descendants, and class hierarchies

Controlling access

Dispatching methods

Abstract class members

Classes and pointers


Creating new classes

Section topics	See also

The difference between component writers and application developers is that component writers create new classes while application developers manipulate instances of classes.

A class is essentially a type. As a programmer, you're always working with types and instances, even if you don't use that terminology. For example, you create variables of a type, such as Integer. Classes are usually more complex than simple data types, but they work the same way: By assigning different values to instances of the same type, you can perform different tasks.

For example, it's quite common to create a form containing two buttons, one labeled OK and one labeled Cancel. Each is an instance of the class TButton, but by assigning different values to their Caption properties and different handlers to their OnClick events, you make the two instances behave differently.


Deriving new classes

Section topics	See also

There are two reasons to derive a new class:

Changing class defaults to avoid repetition

Adding new capabilities to a class

In either case, the goal is to create reusable objects. If you design components with reuse in mind, you can save work later on. Give your classes usable default values, but make them customizable.


Changing class defaults to avoid repetition

Section topics

Most programmers try to avoid repetition. Thus, if you find yourself rewriting the same lines of code over and over, you place the code in a subroutine or function, or build a library of routines that you can use in many programs. The same reasoning holds for components. If you find yourself changing the same properties or making the same method calls, you can create a new component that does these things by default.

For example, suppose that each time you create an application, you add a dialog box to perform a particular operation. Although it's not difficult to recreate the dialog each time, it's also not necessary. You can design the dialog once, set its properties, and install a wrapper component associated with it onto the Component palette. By making the dialog into a reusable component, you not only eliminate a repetitive task, but you encourage standardization and reduce the likelihood of errors each time the dialog is recreated.

Note:	If you want to modify only the published properties of an existing component, or to save specific event handlers for a component or group of components, you may be able to accomplish this more easily by creating a component template.


Adding new capabilities to a class

Section topics

A common reason for creating new components is to add capabilities not found in existing components. When you do this, you derive the new component from either an existing component or an abstract base class, such as TComponent or TControl.

Derive your new component from the class that contains the closest subset of the features you want. You can add capabilities to a class, but you can't take them away; so if an existing component class contains properties that you don't want to include in yours, you should derive from that component's ancestor.

For example, if you want to add features to a list box, you could derive your component from TListBox. However, if you want to add new features but exclude some capabilities of the standard list box, you need to derive your component from TCustomListBox, the ancestor of TListBox. Then you can recreate (or make visible) only the list-box capabilities you want, and add your new features.


Declaring a new component class

Section topics	See also	Example

In addition to standard components, Delphi provides many abstract classes designed as bases for deriving new components. The How do you create components? topic shows the classes you can start from when you create your own components.

To declare a new component class, add a class declaration to the component's unit file.


An example of declaring a new component class

Section topics

Here is the declaration of a simple graphical component:

type

 TSampleShape = class(TGraphicControl)

 end;


Ancestors, descendants, and class hierarchies

Section topics	See also

Application developers take for granted that every control has properties named Top and Left that determine its position on the form. To them, it may not matter that all controls inherit these properties from a common ancestor, TControl. When you create a component, however, you must know which class to derive it from so that it inherits the appropriate features. And you must know everything that your control inherits, so you can take advantage of inherited features without recreating them.

The class from which you derive a component is called its immediate ancestor. Each component inherits from its immediate ancestor, and from the immediate ancestor of its immediate ancestor, and so forth. All of the classes from which a component inherits are called its ancestors; the component is a descendant of its ancestors.

Together, all the ancestor-descendant relationships in an application constitute a hierarchy of classes. Each generation in the hierarchy contains more than its ancestors, since a class inherits everything from its ancestors, then adds new properties and methods or redefines existing ones.

If you do not specify an immediate ancestor, Delphi derives your component from the default ancestor, TObject. TObject is an ancestor of all classes in the Visual Component Library.

The general rule for choosing which object to derive from is simple: Pick the object that contains as much as possible of what you want to include in your new object, but which does not include anything you do not want in the new object. You can always add things to your objects, but you cannot take things out.


Controlling access

Section topics	See also

There are five levels of access control--also called visibility--on properties, methods, and fields. Visibility determines which code can access which parts of the class. By specifying visibility, you define the interface to your components.

The following table shows the levels of visibility, from most restrictive to most accessible:

Visibility	Meaning	Used for
private	Accessible only to code in the unit where the class is defined.	Hiding implementation details.
protected	Accessible to code in the unit(s) where the class and its descendants are defined.	Defining the component writer's interface.
public	Accessible to all code.	Defining the runtime interface.
automated	Accessible to all code. Automation type information is generated.	OLE automation only.
published	Accessible to all code and from the Object Inspector.	Defining the design-time interface.

Declare members as private if you want them to be available only within the class where they are defined; declare them as protected if you want them to be available only within that class and its descendants. Remember, though, that if a member is available anywhere within a unit file, it is available everywhere in that file. Thus, if you define two classes in the same unit, the classes will be able to access each other's private methods. And if you derive a class in a different unit from its ancestor, all the classes in the new unit will be able to access the ancestor's protected methods.


Hiding implementation details

Section topics	See also	Example

Declaring part of a class as private makes that part invisible to code outside the class's unit file. Within the unit that contains the declaration, code can access the part as if it were public.


An example of hiding implementation details

Section topics

Here is an example that shows how declaring a field as private hides it from application developers. The listing shows two form units. Each form has a handler for its OnCreate event that assigns a value to a private field. The compiler allows assignment to the field only in the form where it is declared.

unit HideInfo;
interface

uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs;

type
  TSecretForm = class(TForm)	{ declare new form }

   procedure FormCreate(Sender: TObject);

 private	{ declare private part }
    FSecretCode: Integer;	{ declare a private field }
  end;

var
  SecretForm: TSecretForm;

implementation

procedure TSecretForm.FormCreate(Sender: TObject);

begin
  FSecretCode := 42;	{ this compiles correctly }
end;
end.	{ end of unit }

unit TestHide;	{ this is the main form file }

interface
uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs,
  HideInfo;	{ use the unit with TSecretForm }

type
  TTestForm = class(TForm)

   procedure FormCreate(Sender: TObject);

 end;

var
  TestForm: TTestForm;

implementation

procedure TTestForm.FormCreate(Sender: TObject);

begin
  SecretForm.FSecretCode := 13;	{ compiler stops with "Field identifier expected" }

end;

end.	 { end of unit }

Although a program using the HideInfo unit can use objects of type TSecretForm, it cannot access the FSecretCode field in any of those objects.


Defining the component writer's interface

Section topics	See also

Declaring part of a class as protected makes that part visible only to the class itself and its descendants (and to other classes that share their unit files).

You can use protected declarations to define a component writer's interface to the class. Application units don't have access to the protected parts, but derived classes do. This means that you component writers can change the way a class works without making the details visible to application developers.


Defining the runtime interface

Section topics	See also	Example

Declaring part of a class as public makes that part visible to any code that has access to the class as a whole.

Public parts are available at runtime to all code, so the public parts of a class define its runtime interface. The runtime interface is useful for items that aren't meaningful or appropriate at design time, such as properties that depend on runtime input or which are read-only. Methods that you intend for application developers to call must also be public.


An example of defining the runtime interface

Section topics

Here is an example that shows two read-only properties declared as part of a component's runtime interface:

type
  TSampleComponent = class(TComponent)
  private
    FTempCelsius: Integer;	{ implementation details are private }

   function GetTempFahrenheit: Integer;

 public
    property TempCelsius: Integer read FTempCelsius;	{ properties are public }

   property TempFahrenheit: Integer read GetTempFahrenheit;
  end;

...

function TSampleComponent.GetTempFahrenheit: Integer;

begin

 Result := FTempCelsius * 9 div 5 + 32;

end;


Defining the design-time interface

Section topics	See also	Example

Declaring part of a class as published makes that part public and also generates runtime type information. Among other things, runtime type information allows the Object Inspector to access properties and events.

Because they show up in the Object Inspector, the published parts of a class define that class's design-time interface. The design-time interface should include any aspects of the class that an application developer might want to customize at design time, but must exclude any properties that depend on specific information about the runtime environment.

Read-only properties cannot be part of the design-time interface because the application developer cannot assign values to them directly. Read-only properties should therefore be public, rather than published.


An example of defining the design-time interface

Section topics

Here is an example of a published property called Temperature. Because it is published, it appears in the Object Inspector at design time.

type
  TSampleComponent = class(TComponent)
  private
    FTemperature: Integer;	{ implementation details are private }

 published
    property Temperature: Integer read FTemperature write FTemperature;	{ writable! }
  end;


Dispatching methods

Section topics

Dispatch refers to the way a program determines where a method should be invoked when it encounters a method call. The code that calls a method looks like any other procedure or function call. But classes have different ways of dispatching methods.

The three types of method dispatch are

Static methods

Virtual methods

Dynamic methods


Static methods

Section topics

All methods are static unless you specify otherwise when you declare them. Static methods work like regular procedures or functions. The compiler determines the exact address of the method and links the method at compile time.

The primary advantage of static methods is that dispatching them is very quick. Because the compiler can determine the exact address of the method, it links the method directly. Virtual and dynamic methods, by contrast, use indirect means to look up the address of their methods at runtime, which takes somewhat longer.

A static method does not change when inherited by a descendent class. If you declare a class that includes a static method, then derive a new class from it, the derived class shares exactly the same method at the same address. This means that you cannot override static methods; a static method always does exactly the same thing no matter what class it is called in. If you declare a method in a derived class with the same name as a static method in the ancestor class, the new method simply replaces the inherited one in the derived class.

In the following code, the first component declares two static methods. The second declares two static methods with the same names that replace the methods inherited from the first component.

type
  TFirstComponent = class(TComponent)
    procedure Move;
    procedure Flash;
  end;

 TSecondComponent = class(TFirstComponent)

   procedure Move;	{ different from the inherited method, despite same declaration }
    function Flash(HowOften: Integer): Integer;	{ this is also different }
  end;


Virtual methods

Section topics	See also

Virtual methods employ a more complicated, and more flexible, dispatch mechanism than static methods. A virtual method can be redefined in descendent classes, but still be called in the ancestor class. The address of a virtual method isn't determined at compile time; instead, the object where the method is defined looks up the address at runtime.

To make a method virtual, add the directive virtual after the method declaration. The virtual directive creates an entry in the object's virtual method table, or VMT, which holds the addresses of all the virtual methods in an object type.

When you derive a new class from an existing one, the new class gets its own VMT, which includes all the entries from the ancestor's VMT plus any additional virtual methods declared in the new class.


Overriding methods

Section topics	See also	Example

Overriding a method means extending or refining it, rather than replacing it. A descendent class can override any of its inherited virtual methods.

To override a method in a descendent class, add the directive override to the end of the method declaration.

Overriding a method causes a compilation error if

The method does not exist in the ancestor class.

The ancestor's method of that name is static.

The declarations are not otherwise identical (number and type of arguments parameters differ).


An example of overriding methods

Section topics

The following code shows the declaration of two simple components. The first declares three methods, each with a different kind of dispatching. The other, derived from the first, replaces the static method and overrides the virtual methods.

type
  TFirstComponent = class(TCustomControl)
    procedure Move;	{ static method }

   procedure Flash; virtual;	{ virtual method }

   procedure Beep; dynamic;	{ dynamic virtual method }
  end;

 TSecondComponent = class(TFirstComponent)
    procedure Move;	{ declares new method }

   procedure Flash; override;	{ overrides inherited method }

   procedure Beep; override;	{ overrides inherited method }
  end;


Dynamic methods

Section topics

Dynamic methods are virtual methods with a slightly different dispatch mechanism. Because dynamic methods don't have entries in the object's virtual method table, they can reduce the amount of memory that objects consume. However, dispatching dynamic methods is somewhat slower than dispatching regular virtual methods. If a method is called frequently, or if its execution is time-critical, you should probably declare it as virtual rather than dynamic.

Objects must store the addresses of their dynamic methods. But instead of receiving entries in the virtual method table, dynamic methods are listed separately. The dynamic method list contains entries only for methods introduced or overridden by a particular class. (The virtual method table, in contrast, includes all of the object's virtual methods, both inherited and introduced.) Inherited dynamic methods are dispatched by searching each ancestor's dynamic method list, working backwards through the inheritance tree.

To make a method dynamic, add the directive dynamic after the method declaration.


Abstract class members

Section topics

When a method is declared as abstract in an ancestor class, you must surface it (by redeclaring and implementing it) in any descendent component before you can use the new component in applications. Delphi cannot create instances of a class that contains abstract members. For more information about surfacing inherited parts of classes, see "Creating properties" and "Creating methods."


Classes and pointers

Section topics

Every class (and therefore every component) is really a pointer. The compiler automatically dereferences class pointers for you, so most of the time you don't need to think about this. The status of classes as pointers becomes important when you pass a class as a parameter. In general, you should pass classes by value rather than by reference. The reason is that classes are already pointers, which are references; passing a class by reference amounts to passing a reference to a reference.


Creating properties

Section topics	See also

Properties are the most visible parts of components. The application developer can see and manipulate them at design time and get immediate feedback as the components react in the Form designer. Well-designed properties make your components easier for others to use and easier for you to maintain.

To make the best use of properties in your components, you should understand the following:

Why create properties?

Types of properties

Publishing inherited properties

Defining properties

Creating array properties

Writing property editors


Why create properties?

Section topics	See also

From the application developer's standpoint, properties look like variables. Developers can set or read the values of properties as if they were fields. (About the only thing you can do with a variable that you cannot do with a property is pass it as a var parameter.)

Properties provide more power than simple fields because

Application developers can set properties at design time. Unlike methods, which are available only at runtime, properties let the developer customize components before running an application. Properties can appear in the Object Inspector, which simplifies the programmer's job; instead of handling several parameters to construct an object, you let Delphi read the values from the Object Inspector. The Object Inspector also validates property assignments as soon as they are made.

Properties can hide implementation details. For example, data stored internally in an encrypted form can appear unencrypted as the value of a property; although the value is a simple number, the component may look up the value in a database or perform complex calculations to arrive at it. Properties let you attach complex effects to outwardly simple assignments; what looks like an assignment to a field can be a call to a method which implements elaborate processing.

Properties can be virtual. Hence, what looks like a single property to an application developer may be implemented differently in different components.

A simple example is the Top property of all controls. Assigning a new value to Top doesn't just change a stored value; it repositions and repaints the control. And the effects of setting a property need not be limited to an individual component; for example, setting the Down property of a speed button to True sets Down property of all other speed buttons in its group to False.


Types of properties

Section topics	See also

A property can be of any type. Different types are displayed differently in the Object Inspector, which validates property assignments as they are made at design time.

Property type	Object Inspector treatment
Simple	Numeric, character, and string properties appear as numbers, characters, and strings. The application developer can edit the value of the property directly.
Enumerated	Properties of enumerated types (including Boolean) appear as editable strings. The developer can also cycle through the possible values by double-clicking the value column, and there is a drop-down list that shows all possible values.
Set	Properties of set types appear as sets. By double-clicking on the property, the developer can expand the set and treat each element as a Boolean value (true if it is included in the set).
Object	Properties that are themselves classes often have their own property editors, specified in the component's registration procedure. If the class held by a property has its own published properties, the Object Inspector lets the developer to expand the list (by double-clicking) to include these properties and edit them individually. Object properties must descend from TPersistent.
Array	Array properties must have their own property editors; the Object Inspector has no built-in support for editing them. You can specify a property editor when you register your components.


Publishing inherited properties

Section topics	See also	Example

All components inherit properties from their ancestor classes. When you derive a new component from an existing one, your new component inherits all the properties of its immediate ancestor. If you derive from one of the abstract classes, many of the inherited properties are either protected or public, but not published.

To make a protected or public property available at design time in the Object Inspector, you must redeclare the property as published. Redeclaring means adding a declaration for the inherited property to the declaration of the descendent class.


An example of publishing an inherited property

Section topics

If you derive a component from TWinControl, for example, it inherits the protected Ctl3D property. By redeclaring Ctl3D in your new component, you can change the level of protection to either public or published.

The following code shows a redeclaration of Ctl3D as published, making it available at design time.

type
  TSampleComponent = class(TWinControl)

 published
    property Ctl3D;

 end;

When you redeclare a property, you specify only the property name, not the type and other information described  You can also declare new default values and specify whether to store the property.

Redeclarations can make a property less restricted, but not more restricted. Thus you can make a protected property public, but you cannot hide a public property by redeclaring it as protected.


Defining properties

Section topics	See also

This section shows how to declare new properties and explains some of the conventions followed in the standard components. Topics include

The property declaration

Internal data storage

Direct access

Access methods

Default property values


The property declaration

Section topics	See also	Example

A property is declared in the declaration of its component class. To declare a property, you specify three things:

The name of the property.

The type of the property.

The methods used to read and write the value of the property. If no write method is declared, the property is read-only.

Properties declared in a published section of the component's class declaration are editable in the Object Inspector at design time. Properties declared in a public section are available at runtime and can be read or set in program code.


An example of a property declaration

Section topics

Here is a typical declaration for a property called Count:

type
  TYourComponent = class(TComponent)

 private

   FCount: Integer; { used for internal storage }

   procedure SetCount (Value: Integer); { write method }

 public
    property Count: Integer read FCount write SetCount;

 end;


Internal data storage (properties)
Internal data storage

Section topics	See also

There are no restrictions on how you store the data for a property. In general, however, Delphi components follow these conventions:

Property data is stored in class fields.

The fields used to store property data are private and should be accessed only from within the component itself. Derived components should use the inherited property; they do not need direct access to the property's internal data storage.

Identifiers for these fields consist of the letter F followed by the name of the property. For example, the raw data for the Width property defined in TControl is stored in a field called FWidth.

The principle that underlies these conventions is that only the implementation methods for a property should access the data behind it. If a method or another property needs to change that data, it should do so through the property, not by direct access to the stored data. This ensures that the implementation of an inherited property can change without invalidating derived components.


Direct access

Section topics	See also	Example

The simplest way to make property data available is direct access. That is, the read and write parts of the property declaration specify that assigning or reading the property value goes directly to the internal-storage field without calling an access method. Direct access is useful when you want to make a property available in the Object Inspector but changes to its value trigger no immediate processing.

It is common to have direct access for the read part of a property declaration but use an access method for the write part. This allows the status of the component to be updated when the property value changes.


An example of a property that uses direct access

Section topics

The following component-type declaration shows a property that uses direct access for both the read and the write parts.

type
  TSampleComponent = class(TComponent)

 private { internal storage is private}

   FMyProperty: Boolean; { declare field to hold property value }

 published { make property available at design time }

   property MyProperty: Boolean read FMyProperty write FMyProperty;

 end;


Access methods (properties)
Access methods

Section topics	Example

You can specify an access method instead of a field in the read and write parts of a property declaration. Access methods should be protected, and are usually declared as virtual; this allows descendent components to override the property's implementation.

Avoid making access methods public. Keeping them protected ensures that application developers don't inadvertently modify a property by calling one of these methods.

The read method

The read method for a property is a function that takes no parameters (except as noted below) and returns a value of the same type as the property. By convention, the function's name is Get followed by the name of the property. For example, the read method for a property called Count would be GetCount. The read method manipulates the internal storage data as needed to produce the value of the property in the appropriate type.

The only exceptions to the no-parameters rule are for array properties and properties that use index specifiers, both of which pass their index values as parameters. (Use index specifiers to create a single read method that is shared by several properties. For more information about index specifiers, see the Object Pascal Language Guide.)

If you don't declare a read method, the property is write-only. Write-only properties are seldom used.

The write method

The write method for a property is a procedure that takes a single parameter (except as noted below) of the same type as the property. The parameter can be passed by reference or by value, and can have any name you choose. By convention, the write method's name is Set followed by the name of the property. For example, the write method for a property called Count would be SetCount. The value passed in the parameter becomes the new value of the property; the write method must perform any manipulation needed to put the appropriate data in the property's internal storage.

The only exceptions to the single-parameter rule are for array properties and properties that use index specifiers, both of which pass their index values as a second parameter. (Use index specifiers to create a single write method that is shared by several properties. For more information about index specifiers, see the Object Pascal Language Guide.)

If you don't declare a write method, the property is read-only.

Write methods commonly test whether a new value differs from the current value before changing the property. For example, here's a simple write method for an integer property called Count that stores its current value in a field called FCount.

procedure TMyComponent.SetCount(Value: Integer);
begin

 if Value <> FCount then
  begin
    FCount := Value;

   Update;
  end;
end;


Default property values

Section topics	See also	Example

When you declare a property, you can specify a default value for it. Delphi uses the default value to determine whether to store the property in a form file. (For information on storing properties and the importance of default values, see) If you do not specify a default value for a property, Delphi always stores the property.

To specify a default value for a property, append the default directive to the property's declaration (or redeclaration), followed by the default value. For example,

property Cool Boolean read GetCool write SetCool default True;

Note:	Declaring a default value does not set the property to that value. The component's constructor method should initialize property values when appropriate. However, since objects always initialize their fields to 0, it is not strictly necessary for the constructor to set integer properties to 0, string properties to null, or Boolean properties to False.

Note:	Do not confuse the default value of a property with declaring a property as the default proprety of an object.  When no value follows the default directive, the property is the default property of the object.  When a property is the default property, the property can be referenced without specifying the property name.

Specifying no default value

When redeclaring a property, you can specify that the property has no default value, even if the inherited property specified one.

To designate a property as having no default value, append the nodefault directive to the property's declaration. For example,

property FavoriteFlavor string nodefault;

When you declare a property for the first time, there is no need to include nodefault. The absence of a declared default value means that there is no default.


An example of a property with a default value

Section topics

For more information about using the default value of a property, see Specifying default values.Here is the declaration of a component that includes a single Boolean property called IsTrue with a default value of True. Below the declaration (in the implementation section of the unit) is the constructor that initializes the property.

type
  TSampleComponent = class(TComponent)

 private
    FIsTrue: Boolean;

 public
    constructor Create(AOwner: TComponent); override;

 published
    property IsTrue: Boolean read FIsTrue write FIsTrue default True;

 end;

...

constructor TSampleComponent.Create(AOwner: TComponent);

begin
  inherited Create(AOwner);	{ call the inherited constructor }

 FIsTrue := True;	{ set the default value }

end;


Creating array properties

Section topics	See also	Example

Some properties lend themselves to being indexed like arrays. For example, the Lines property of TMemo is an indexed list of the strings that make up the text of the memo; you can treat it as an array of strings. Lines provides natural access to a particular element (a string) in a larger set of data (the memo text).

Array properties are declared like other properties, except that

The declaration includes one or more indexes with specified types. The indexes can be of any type.

The read and write parts of the property declaration, if specified, must be methods. They cannot be fields.

The read and write methods for an array property take additional parameters that correspond to the indexes. The parameters must be in the same order and of the same type as the indexes specified in the declaration.

There are a few important differences between array properties and arrays. Unlike the index of an array, the index of an array property does not have to be an integer type. You can index a property on a string, for example. In addition, you can reference only individual elements of an array property, not the entire range of the property.


An example of an array property

Section topics

The following example shows the declaration of a property that returns a string based on an integer index.

type
  TDemoComponent = class(TComponent)

 private
    function GetNumberName(Index: Integer): string;

 public
    property NumberName[Index: Integer]: string read GetNumberName;
  end;

...

function TDemoComponent.GetNumberName(Index: Integer): string;
begin
  Result := 'Unknown';

 case Index of
    -MaxInt..-1: Result := 'Negative';

   0: Result := 'Zero';

   1..100: Result := 'Small';

   101..MaxInt: Result := 'Large';
  end;
end;


Writing property editors

Section topics	See also

The Object Inspector provides default editing for all properties except arrays. You can create editors for array properties as well as alternative editors for other properties. Custom editors can be registered so that they apply only to properties in components you write, or to all properties of a specified type.

A property editor can operate in two ways: displaying and allowing the user to edit the current value as a text string, and displaying a dialog box that permits some other kind of editing. Depending on the property being edited, you may find it useful to provide either or both of these methods.

Writing a property editor can involve

Deriving a property-editor class.

Editing the property as text.

Editing the property as a whole.

Specifying editor attributes.

Registering the property editor.


Deriving a property-editor class

Section topics	See also	Example

To create a property editor, start by deriving a new class from one of the existing property-editor types. The DSGNINTF.PAS file defines several kinds of property editor, all of which descend from TPropertyEditor. Your property-editor class can descend directly from TPropertyEditor or from one of the property-editor types described inthe following table.

Type	Properties edited
TOrdinalProperty	All ordinal-property editors (for integer, character, and enumerated properties) descend from TOrdinalProperty.
TIntegerProperty	All integer types, including predefined and user-defined subranges.
TCharProperty	Character types, including subranges (such as 'A'..'Z').
TEnumProperty	All enumerated types.
TFloatProperty	All floating-point numbers.
TStringProperty	Strings, including strings of specified length, such as string[20].
TSetElementProperty	Individual elements in sets, shown as Boolean values.
TSetProperty	All sets. Sets are not directly editable, but can expand into a list of set-element properties.
TClassProperty	Classes. Displays the name of the class and expands to display the class's published properties.
TMethodProperty	Method pointers, especially events.
TComponentProperty	Components in the same form. Does not edit the component's properties, but can point to a specific component of a compatible type.
TColorProperty	Component colors. Shows color constants if applicable, otherwise displays hexadecimal value. Drop-down list contains the color constants. Double-clicking opens a color-selection dialog.
TFontNameProperty	Font names. The drop-down list displays all currently installed fonts.
TFontProperty	Fonts. Allows expansion of individual font properties as well as access to the font dialog box.
Note:	The DsgnIntf unit also defines some specialized editors used by unique properties such as Name. The property editors listed here are the ones that are most useful for component writers.


An example of a property-editor class

Section topics

One of the simplest property editors is TFloatProperty, the editor for properties that are floating-point numbers. Here is its declaration:

type
  TFloatProperty = class(TPropertyEditor)

 public
   function AllEqual: Boolean; override;

   function GetValue: string; override;

   procedure SetValue(const Value: string); override;

 end;


Editing the property as text

Section topics

All properties need to provide a string representation of their values for the Object Inspector to display. Most properties also allow the developer to type in a new value at design time. Property-editor classes provide virtual methods that you can override to convert between the text representation and the actual value.

The methods you override are called GetValue and SetValue. Your property editor also inherits a set of methods used for assigning and reading different sorts of values, as shown inthe following table

Property type	"Get" method	"Set" method
Floating point	GetFloatValue	SetFloatValue
Method pointer (event)	GetMethodValue	SetMethodValue
Ordinal type	GetOrdValue	SetOrdValue
String	GetStrValue	SetStrValue

When you override GetValue, call one of the "Get" methods. When you override a SetValue, you will call one of the "Set" methods.

Displaying the property value

The GetValue method returns a string that represents the current value of the property. The Object Inspector uses this string in the value column for the property. By default, GetValue returns "unknown". To provide a string representation of your property, override the property editor's GetValue method. If the property doesn't hold a string, your GetValue must convert the value into a string.

Setting the property value

The property editor's SetValue method takes a string typed into the Object Inspector, converts it to the appropriate type, and sets the value of the property. To read string values into your properties, override the property editor's SetValue method. Your SetValue should convert the string and validate it before calling one of the "Set" methods; when a string does not represent a proper value for the property, SetValue should raise an exception and not use the string.

Here are the GetValue and SetValue methods for TIntegerProperty. Integer is an ordinal type, so GetValue calls GetOrdValue and converts the result to a string. SetValue converts the string to an integer, performs range checking, and calls SetOrdValue.

function TIntegerProperty.GetValue: string;

begin

 Result := IntToStr(GetOrdValue);

end;

procedure TIntegerProperty.SetValue(const Value: string);

var
  L: Longint;

begin

 L := StrToInt(Value);	{ convert string to number }

 with GetTypeData(GetPropType)^ do	{ this uses compiler data for type Integer }

   if (L < MinValue) or (L > MaxValue) then	{ make sure it's in range... }
      raise EPropertyError.Create(	{ ...otherwise, raise exception }
        FmtLoadStr(SOutOfRange, [MinValue, MaxValue]));

 SetOrdValue(L);	{ if in range, go ahead and set value }
end;


Editing the property as a whole

Section topics

Instead of (or in addition to) text editing in the Object Inspector, you can provide a dialog box for setting a property. The most common use of property editors is for properties that are classes. An example is the Font property, which has an associated editor dialog that lets the developer choose all the attributes of the font at once.

When the user clicks the '...' button next to the property or double-clicks the value column, the Object Inspector calls the property editor's Edit method. To provide an editor dialog, override the property-editor class's Edit method. Edit methods use the same "Get" and "Set" methods used in GetValue and SetValue methods; an Edit method calls both a "Get" method and a "Set" method. Because the editor is type-specific, there is usually no need to convert the property values to strings.

Within your implementation of the Edit method, follow these steps:

1.	Construct the editor you are using for the property.

2.	Read the current value and assign it to the property using a "Get" method.

3.	When the user selects a new value, assign that value to the property using a "Set" method.

4.	Destroy the editor.

The Color properties found in most components use the standard Windows color dialog box as a property editor. Here is the Edit method from TColorProperty, which invokes the dialog box and uses the result:

procedure TColorProperty.Edit;

var
  ColorDialog: TColorDialog;

begin
  ColorDialog := TColorDialog.Create(Application); { construct the editor }

 try
    ColorDialog.Color := GetOrdValue; { use the existing value }

   if ColorDialog.Execute then { if the user OKs the dialog... }

     SetOrdValue(ColorDialog.Color); { ...use the result to set value }

 finally
    ColorDialog.Free; { destroy the editor }

 end;
end;


Specifying editor attributes

Section topics	See also	Example

The property editor must provide information that the Object Inspector can use to determine what tools to display. For example, the Object Inspector needs to know whether the property has subproperties or can display a list of possible values.

To specify editor attributes, override the property editor's GetAttributes method. GetAttributes returns a set of values of type TPropertyAttributes that can include any or all of the following:

Flag	Related method	Meaning if included
paValueList	GetValues	The editor can give a list of enumerated values.
paSubProperties	GetProperties	The property has subproperties that can be displayed.
paDialog	Edit	The editor can display a dialog box for editing the entire property.
paMultiSelect	N/A	The property should display when the user selects more than one component.
paAutoUpdate	SetValue	The component should be updated after every change instead of waiting for approval of the value.
paSortList	N/A	The Object Inspector should sort the value list.
paReadOnly	N/A	The property value cannot be modified at design time.
paRevertable	N/A	Enables the Revert to Inherited menu item on the Object Inspector's context menu. The menu item tells the property editor to discard the current property value and return to some previously established default or standard value.


An example of specifying editor attributes

Section topics

Color properties are more versatile than most, in that they allow several ways for users to choose them in the Object Inspector: typing, selection from a list, and a customized editor. TColorProperty's GetAttributes method, therefore, includes several attributes in its return value:

function TColorProperty.GetAttributes: TPropertyAttributes;

begin

 Result := [paMultiSelect, paDialog, paValueList];

end;


Registering the property editor

Section topics	See also	Example

Once you create a property editor, you need to register it with Delphi. Registering a property editor associates a type of property with a specific property editor. You can register the editor with all properties of a given type or just with a particular property of a particular type of component.

To register a property editor, call the RegisterPropertyEditor procedure, which takes four parameters:

A type-information pointer for the type of property to edit. This is always a call to the built-in function TypeInfo, such as TypeInfo(TMyComponent).

The class of component to which this editor applies. If this parameter is nil, the editor applies to all properties of the given type, in any component class.

The name of the property. This parameter has meaning only if the previous parameter specifies a particular class of component. In that case, you can specify a property in that component's class to which the editor applies.

The type of property editor to use for editing the specified property.


An example of registering a property editor

Section topics

Here is an excerpt from the procedure that registers the editors for the standard components on the Component palette:

procedure Register;

begin

 RegisterPropertyEditor(TypeInfo(TComponent), nil, '', TComponentProperty);

 RegisterPropertyEditor(TypeInfo(TComponentName), TComponent, 'Name',     TComponentNameProperty);

 RegisterPropertyEditor(TypeInfo(TMenuItem), TMenu, '', TMenuItemProperty);

end;

The three statements in this procedure cover the different uses of RegisterPropertyEditor:

The first statement is the most typical. It registers the property editor TComponentProperty for all properties of type TComponent (or descendants of TComponent that do not have their own editors registered). Usually, you create a property editor for a particular type, and you want to use it for all properties of that type; so the second and third parameters are nil and an empty string, respectively.

The second statement registers an editor for a specific property in a specific type of component. In this case, the editor is for the Name property of all components.

The third statement is more specific than the first, but not as specific as the second. It registers an editor for all properties of type TMenuItem in components of type TMenu.


Creating events

Section topics 	See also

An event is a link between an occurrence in the system (such as a user action or a change in focus) and a piece of code that responds to that occurrence. The responding code is an event handler, and is nearly always written by the application developer. Events let application developers customize the behavior of components without having to change the classes themselves.

Events for the most common user actions (such as mouse actions) are built into all the standard components, but you can also define new events. To create events in a component, you need to understand the following:

What are events?

Implementing the standard events

Defining your own events

Events are implemented as properties, so you should already be familiar with "Creating properties" before you attempt to create or change a component's events.


What are events?

Section topics 	See also

An event is a mechanism that links an occurrence to some code. More specifically, an event is a method pointer that points to a method in a specific class instance.

From the application developer's perspective, an event is just a name related to a system occurrence, such as OnClick, to which specific code can be attached. For example, a push button called Button1 has an OnClick method. By default, Delphi generates an event handler called Button1Click in the form that contains the button and assigns it to OnClick. When a click event occurs in the button, the button calls the method assigned to OnClick, in this case, Button1Click.To write an event, you need to understand the following:

Events are method pointers

Events are properties

Event types are method-pointer types

Event handlers are optional


Events are method pointers

Section topics

Delphi uses method pointers to implement events. A method pointer is a special pointer type that points to a specific method in an instance object. As a component writer, you can treat the method pointer as a placeholder: When your code detects that an event occurs, you call the method (if any) specified by the user for that event.

Method pointers work just like any other procedural type, but they maintain a hidden pointer to an object. When the application developer assigns a handler to a component's event, the assignment is not just to a method with a particular name, but rather to a method in a specific instance object. That object is usually the form that contains the component, but it need not be.


Events are properties

Section topics

Components use properties to implement their events. Unlike most other properties, events don't use methods to implement their read and write parts. Instead, event properties use a private class field of the same type as the property.

By convention, the field's name is the name of the property preceded by the letter F. For example, the OnClick method's pointer is stored in a field called FOnClick of type TNotifyEvent, and the declaration of the OnClick event property looks like this:

type
  TControl = class(TComponent)
  private
    FOnClick: TNotifyEvent;	{ declare a field to hold the method pointer }

   ...

 protected
    property OnClick: TNotifyEvent read FOnClick write FOnClick;
  end;

As with any other property, you can set or change the value of an event at runtime. The main advantage to having events be properties, however, is that component users can assign handlers to events at design time, using the Object Inspector.


Event types are method-pointer types

Section topics

Because an event is a pointer to an event handler, the type of the event property must be a method-pointer type. Similarly, any code to be used as an event handler must be an appropriately typed method of an object.

All event-handler methods are procedures. To be compatible with an event of a given type, an event-handler method must have the same number and type of parameters, in the same order, passed in the same way.

Delphi defines method types for all its standard events. When you create your own events, you can use an existing type if that's appropriate, or define one of your own.

Event-handler types are procedures

Although the compiler allows you to declare method-pointer types that are functions, you should never do so for handling events. Because an empty function returns an undefined result, an empty event handler that was a function might not always be valid. For this reason, all your events and their associated event handlers should be procedures.

Although an event handler cannot be a function, you can still get information from the application developer's code using var parameters. When doing this, make sure you assign a valid value to the parameter before calling the handler so you don't require the user's code to change the value.

An example of passing var parameters to an event handler is the OnKeyPress event, of type TKeyPressEvent. TKeyPressEvent defines two parameters, one to indicate which object generated the event, and one to indicate which key was pressed:

type

 TKeyPressEvent = procedure(Sender: TObject; var Key: Char) of object;

Normally, the Key parameter contains the character pressed by the user. Under certain circumstances, however, the user of the component may want to change the character. One example might be to force all characters to uppercase in an editor. In that case, the user could define the following handler for keystrokes:

procedure TForm1.Edit1KeyPressed(Sender: TObject; var Key: Char);
begin
  Key := UpCase(Key);
end;

You can also use var parameters to let the user override the default handling.


Event handlers are optional

Section topics

When creating events, remember that developers using your components may not attach handlers to them. This means that your component shouldn't fail or generate errors simply because there is no handler attached to a particular event. (The mechanics of calling handlers and dealing with events that have no attached handler are explained in "Calling the event".)

Events happen almost constantly in a Windows application. Just moving the mouse pointer across a visual component makes Windows send numerous mouse-move messages, which the component translates into OnMouseMove events. In most cases, developers don't want to handle the mouse-move events, and this should not cause a problem. So the components you create should require handlers for their events.

Moreover, application developers can write any code they want in an event handler. The components in the VCL have events written in such a way as to minimize the chance of an event-handler generating errors. Obviously, you can't protect against logic errors in application code, but you can ensure that data structures are initialized before calling events so that application developers don't try to access invalid data.


Implementing the standard events

Section topics 	See also

The controls that come with Delphi inherit events for the most common Windows occurrences. These are called the standard events. Although all these events are built into the controls, they are often protected, meaning developers cannot attach handlers to them. When you create a control, you can choose to make events visible to users of your control.

There are three things you need to consider when incorporating the standard events into your controls:

Identifying standard events

Making events visible

Changing the standard event handling


Identifying standard events

Section topics 	See also

There are two categories of standard events: those defined for all controls and those defined only for the standard windowed controls.

Standard events for all controls

The most basic events are defined in the class TControl. All controls, whether windowed, graphical, or custom, inherit these events. The following table lists events available in all controls:

OnClick	OnDragDrop	OnEndDrag	OnMouseMove
OnDblClick	OnDragOver	OnMouseDown	OnMouseUp

The standard events have corresponding protected virtual methods declared in TControl, with names that correspond to the event names. For example, OnClick events call a method named Click, and OnEndDrag events call a method named DoEndDrag.

Standard events for standard controls

In addition to the events common to all controls, standard windowed controls (those that descend from TWinControl) have the following events:

OnEnter	OnKeyDown	OnKeyPress
OnKeyUp	OnExit

Like the standard events in TControl, the windowed-control events have corresponding methods.


Making events visible

Section topics 	See also	Example

The declarations of the standard events in TControl and TWinControl are protected, as are the methods that correspond to them. If you are inheriting from one of these abstract classes and want to make their events accessible at runtime or design time, you need to redeclare the events as either public or published.

Redeclaring a property without specifying its implementation keeps the same implementation methods, but changes the protection level. You can, therefore, take an event that's defined in TControl but not made visible, and surface it by declaring it as public or published.


An example of making an event visbile

Section topics

For example, to create a component that surfaces the OnClick event at design time, you would add the following to the component's class declaration.

type

 TMyControl = class(TCustomControl)

 ...

 published

   property OnClick;

 end;


Changing the standard event handling

Section topics 	See also	Example

If you want to change the way your component responds to a certain kind of event, you might be tempted to write some code and assign it to the event. As an application developer, that's exactly what you would do. But when you're creating a component, you must keep the event available for developers who use the component.

This is the reason for the protected implementation methods associated with each of the standard events. By overriding the implementation method, you can modify the internal event handling; and by calling the inherited method you can maintain the standard handling, including the event for the application developer's code.

The order in which you call the methods is significant. As a rule, call the inherited method first, allowing the application developer's event-handler to execute before your customizations (and in some cases, to keep the customizations from executing). There may be times when you want to execute your code before calling the inherited method, however. For example, if the inherited code is somehow dependent on the status of the component and your code changes that status, you should make the changes and then allow the user's code to respond to them.


An example of changing the standard event handling

Section topics

Supposeyou're writing a component and you want to modify the way it responds to mouse clicks. Instead of assigning a handler to the OnClick event as a application developer would, you would override the protected method Click:

procedure click override { forward declaration }

...

procedure TMyControl.Click;

begin
  inherited Click;	{ perform standard handling, including calling handler }
  { your customizations go here }
end;


Defining your own events

Section topics 	See also

Defining entirely new events is relatively unusual. There are times, however, when a component introduces behavior that is entirely different from that of any other component, so you'll need to define an event for it.

There are the issues you'll need to consider when defining an event:

Triggering the event

Defining the handler type

Declaring the event

Calling the event


Triggering the event

Section topics 	See also

You need to know what triggers the event. For some events, the answer is obvious. For example, a mouse-down event occurs when the user presses the left button on the mouse and Windows sends a WM_LBUTTONDOWN message to the application. Upon receiving that message, a component calls its MouseDown method, which in turn calls any code the user has attached to the OnMouseDown event.

But some events are less clearly tied to specific external occurrences. For example, a scroll bar has an OnChange event, which is triggered by several kinds of occurrence, including keystrokes, mouse clicks, and changes in other controls. When defining your events, you must ensure that all the appropriate occurrences call the proper events.

Two kinds of event

There are two kinds of occurrence you might need to provide events for: user interactions and state changes. User-interaction events are nearly always triggered by a message from Windows, indicating that the user did something your component may need to respond to. State-change events may also be related to messages from Windows (focus changes or enabling, for example), but they can also occur through changes in properties or other code. You have total control over the triggering of the events you define. Define the events with care so that developers are able to understand and use them.


Defining the handler type

Section topics 	See also

Once you determine when the event occurs, you must define how you want the event handled. This means determining the type of the event handler. In most cases, handlers for events you define yourself are either simple notifications or event-specific types. It is also possible to get information back from the handler.

Simple notifications

A notification event tells you only that the particular event happened, with no specific information about when or where. Notifications use the type TNotifyEvent, which carries only one parameter, the sender of the event. All a handler for a notification "knows" about the event is what kind of event it was, and what component the event happened to. For example, click events are notifications. When you write a handler for a click event, all you know is that a click occurred and which component was clicked.

Notification is a one-way process. There is no mechanism to provide feedback or prevent further handling of a notification.

Event-specific handlers

In some cases, it's not enough to know which event happened and what component it happened to. For example, if the event is a key-press event, it's likely that the handler will want to know which key the user pressed. In these cases, you need handler types that include parameters for additional information.

If your event was generated in response to a message, it is likely that the parameters you pass to the event handler come directly from the message parameters.

Returning information from the handler

Because all event handlers are procedures, the only way to pass information back from a handler is through a var parameter. Your components can use such information to determine how or whether to process an event after the user's handler executes.

For example, all the key events (OnKeyDown, OnKeyUp, and OnKeyPress) pass by reference the value of the key pressed in a parameter named Key. The event handler can change Key so that the application sees a different key as being involved in the event. This is a way to force typed characters to uppercase, for example.


Declaring the event

Section topics 	See also

Once you've determined the type of your event handler, you're ready to declare the method pointer and the property for the event. Be sure to give the event a meaningful and descriptive name so that users can understand what the event does. Try to be consistent with names of similar properties in other components.

Event names start with "On"

The names of most events in Delphi begin with "On." This is just a convention; the compiler doesn't enforce it. The Object Inspector determines that a property is an event by looking at the type of the property: all method-pointer properties are assumed to be events and appear on the Events page.

Developers expect to find events in the alphabetical list of names starting with "On." Using other kinds of names is likely to confuse them.


Calling the event

Section topics 	See also

You should centralize calls to an event. That is, create a virtual method in your component that calls the application's event handler (if it assigns one) and provides any default handling.

Putting all the event calls in one place ensures that someone deriving a new component from yours can customize event handling by overriding a single method, rather than searching through your code for places where you call the event.

There are two other considerations when calling the event:

Empty handlers must be valid

Users can override default handling


Empty handlers must be valid

Section topics 	See also	Example

You should never create a situation in which an empty event handler causes an error, nor should the proper functioning of your component depend on a particular response from the application's event-handling code.


An example of calling an event handler

Section topics

An empty handler should produce the same result as no handler at all. So the code for calling an application's event handler should look like this:

if Assigned(OnClick) then OnClick(Self);
{ perform default handling }

You should never have something like this:

if Assigned(OnClick) then OnClick(Self)
else { perform default handling };


Users can override default handling

Section topics 	See also	Example

For some kinds of events, developers may want to replace the default handling or even suppress all responses. To allow this, you need to pass an argument by reference to the handler and check for a certain value when the handler returns.

This is in keeping with the rule that an empty handler should have the same effect as no handler at all. Because an empty handler won't change the values of arguments passed by reference, the default handling always takes place after calling the empty handler.


An example of overriding default handling

Section topics

When handling key-press events, for example, application developers can suppress the component's default handling of the keystroke by setting the var parameter Key to a null character (#0). The logic for supporting this looks like

if Assigned(OnKeyPress) then OnKeyPress(Self, Key);
if Key <> #0 then	{ perform default handling }

The actual code is a little different from this because it deals with Windows messages, but the logic is the same. By default, the component calls any user-assigned handler, then performs its standard handling. If the user's handler sets Key to a null character, the component skips the default handling.


Creating methods

Section topics	See also

Component methods are procedures and functions built into the structure of a class. Although there are essentially no restrictions on what you can do with the methods of a component, Delphi uses some standards that you should follow. These guidelines include

Avoiding dependencies

Naming methods

Protecting methods

Making methods virtual

Declaring methods

In general, components should not contain many methods and you should minimize the number of methods that an application needs to call. The features you might be inclined to implement as methods are often better encapsulated into properties. Properties provide an interface that suits the Delphi environment and are accessible at design time.


Avoiding dependencies

Section topics	See also

At all times when writing components, minimize the preconditions imposed on the developer. To the greatest extent possible, developers should be able to do anything they want to a component, whenever they want to do it. There will be times when you can't accommodate that, but your goal should be to come as close as possible.

This list gives you an idea of the kinds of dependencies to avoid:

Methods that the user must call to use the component.

Methods that must execute in a particular order.

Methods that put the component into a state or mode where certain events or methods could be invalid

The best way to handle these situations is to ensure that you provide ways out of them. For example, if calling a method puts your component into a state where calling another method might be invalid, then write that second method so that if an application calls it when the component is in a bad state, the method corrects the state before executing its main code. At a minimum, you should raise an exception in cases when a user calls a method that is invalid.

In other words, if you create a situation where parts of your code depend on each other, the burden should be on you to be sure that using the code in incorrect ways does not cause problems. A warning message, for example, is preferable to a system failure if the user doesn't accommodate your dependencies.


Naming methods

Section topics	See also

Delphi imposes no restrictions on what you name methods or their parameters. There are a few conventions that make methods easier for application developers, however. Keep in mind that the nature of a component architecture dictates that many different kinds of people might use your components.

If you're accustomed to writing code that only you or a small group of programmers use, you might not think too much about how you name things. It is a good idea to make your method names clear because people unfamiliar with your code (and even unfamiliar with coding) might have to use your components.

Here are some suggestions for making clear method names:

Make names descriptive. Use meaningful verbs.

A name like PasteFromClipboard is much more informative than simply Paste or PFC. ReadFileNames is much more helpful than DoFiles.

Function names should reflect the nature of what they return.

Although it might be obvious to you as a programmer that a function named X returns the horizontal position of something, a name like GetHorizontalPosition is more universally understandable.

As a final consideration, make sure the method really needs to be a method. A good guideline is that method names have verbs in them. If you find that you create a lot of methods that do not have verbs in their names, consider whether those methods ought to be properties.


Protecting methods

Section topics	See also

All parts of classes, including fields, methods, and properties, have a level of protection or "visibility," as explained in "Controlling access". Choosing the appropriate visibility for a method is simple.

Most methods you write in your components are public or protected. You rarely need to make a method private, unless it is truly specific to that type of component, to the point that even derived components should not have access to it.


Methods that should be public

Section topics	See also

Any method that application developers need to call must be declared as public. Keep in mind that most method calls occur in event handlers, so methods should avoid tying up system resources or putting Windows in a state where it can't respond to the user.

Note:	Constructors and destructors should always be public.


Methods that should be protected

Section topics	See also

Any implementation methods for the component should be protected so that applications can't call them at the wrong time. If you have methods that application code should not call, but that are called in derived classes, declare them as protected.

For example, suppose you have a method that relies on having certain data set up for it beforehand. If you make that method public, there's a chance that applications will call it before setting up the data. On the other hand, by making it protected, you ensure that applications can't call it directly. You can then set up other, public methods that ensure that data setup occurs before calling the protected method.

Property-implementation methods should be declared as virtual protected methods. Methods that are so declared allow the application developers to override the property implementation, either augmenting its functionality or replacing it completely. Such properties are fully polymorphic. Keeping access methods protected ensures that developers don't accidentally call them, inadvertently modifying a property.


Abstract methods

Section topics

Sometimes a method is declared as abstract in a Delphi component. In the VCL, abstract methods occur only in classes whose names begin with "custom", such as TCustomGrid. Such classes are themselves abstract, in the sense that they are intended only for deriving descendent classes.

You cannot create an instance object of a class that contains an abstract member. The abstract directive is used to indicate parts of classes that should be surfaced and defined in descendent components; it forces Component writers to redeclare the abstract member in descendent classes before actual instances of the class can be created.


Making methods virtual

Section topics	See also

You make methods virtual when you want different types to be able to execute different code in response to the same method call.

If you create components intended to be used directly by application developers, you can probably make all your methods nonvirtual. On the other hand, if you create abstract components from which other components will be derived, consider making the added methods virtual. This way, derived components can override the inherited virtual methods.


Declaring methods

Section topics	See also	Example

Declaring a method in a component is the same as declaring any class method.

To declare a new method in a component, you do two things:

Add the declaration to the component's object-type declaration.

Implement the method in the implementation part of the component's unit.


An example of declaring methods

Section topics

The following code shows a component that defines two new methods, one protected static method and one public virtual method.

type
  TSampleComponent = class(TControl)
  protected
    procedure MakeBigger;	{ declare protected static method }

 public
    function CalculateArea: Integer; virtual;	{ declare public virtual method }
  end;

...

implementation
...
procedure TSampleComponent.MakeBigger;	{ implement first method }
begin
  Height := Height + 5;

 Width := Width + 5;
end;

function TSampleComponent.CalculateArea: Integer;	{ implement second method }
begin
  Result := Width * Height;
end;


Using graphics in components

Section topics 	See also

Windows provides a powerful Graphics Device Interface (GDI) for drawing device-independent graphics. The GDI, however, imposes extra requirements on the programmer, such as managing graphic resources. Delphi takes care of all the GDI drudgery, allowing you to focus on productive work instead of searching for lost handles or unreleased resources.

As with any part of the Windows API, you can call GDI functions directly from your Delphi application. But you will probably find that using Delphi's encapsulation of the graphic functions is faster and easier.

The topics in this section include:

Overview of graphics

Using the canvas

Working with pictures

Offscreen bitmaps

Responding to changes


Overview of graphics

Section topics 	See also	Example

Delphi encapsulates the Windows GDI at several levels. The most important to you as a component writer is the way components display their images on the screen. When calling GDI functions directly, you need to have a handle to a device context, into which you have selected various drawing tools such as pens, brushes, and fonts. After rendering your graphic images, you must restore the device context to its original state before disposing of it.

Instead of forcing you to deal with graphics at a detailed level, Delphi provides a simple yet complete interface: your component's Canvas property. The canvas ensures that it has a valid device context, and releases the context when you're not using it. Similarly, the canvas has its own properties representing the current pen, brush, and font.

The canvas manages all these resources for you, so you need not concern yourself with creating, selecting, and releasing things like pen handles. You just tell the canvas what kind of pen it should use, and it takes care of the rest.

One of the benefits of letting Delphi manage graphic resources is that it can cache resources for later use, which can speed up repetitive operations. For example, if you have a program that repeatedly creates, uses, and disposes of a particular kind of pen tool, you need to repeat those steps each time you use it. Because Delphi caches graphic resources, chances are good that a tool you use repeatedly is still in the cache, so instead of having to recreate a tool, Delphi uses an existing one.


An example of simplified graphics code

Section topics

Here are two examples of how simple Delphi's graphics code can be. The first uses standard GDI functions to draw a yellow ellipse outlined in blue on a window in an application written with ObjectWindows. The second uses a canvas to draw the same ellipse in an application written with Delphi.

procedure TMyWindow.Paint(PaintDC: HDC; var PaintInfo: TPaintStruct);
var
  PenHandle, OldPenHandle: HPEN;
  BrushHandle, OldBrushHandle: HBRUSH;

begin
  PenHandle := CreatePen(PS_SOLID, 1, RGB(0, 0, 255));	{ create blue pen }

 OldPenHandle := SelectObject(PaintDC, PenHandle);	{ tell DC to use blue pen }

 BrushHandle := CreateSolidBrush(RGB(255, 255, 0));	{ create a yellow brush }

 OldBrushHandle := SelectObject(PaintDC, BrushHandle);	{ tell DC to use yellow brush }

 Ellipse(HDC, 10, 10, 50, 50);	{ draw the ellipse }

 SelectObject(OldBrushHandle);	{ restore original brush }

 DeleteObject(BrushHandle);	{ delete yellow brush }

 SelectObject(OldPenHandle);	{ restore original pen }

 DeleteObject(PenHandle);	{ destroy blue pen }
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
  with Canvas do
  begin
    Pen.Color := clBlue;	{ make the pen blue }

   Brush.Color := clYellow;	{ make the brush yellow }

   Ellipse(10, 10, 50, 50);	{ draw the ellipse }
  end;
end;


Using the canvas

Section topics 	See also

The canvas class encapsulates Windows graphics at several levels, including high-level functions for drawing individual lines, shapes, and text; intermediate properties for manipulating the drawing capabilities of the canvas; and low-level access to the Windows GDI.

The following table summarizes the capabilities of the canvas.

Level	Operation	Tools
High	Drawing lines and shapes.	Methods such as MoveTo, LineTo, Rectangle, and Ellipse.
	Displaying and measuring text.	TextOut, TextHeight, TextWidth, and TextRectmethods.
	Filling areas.	FillRect and FloodFill methods.
Intermediate	Customizing text and graphics.	Pen, Brush, and Font properties.
	Manipulating pixels.	Pixels property.
	Copying and merging images.	Draw, StretchDraw, BrushCopy, and CopyRect methods; CopyMode property.
Low	Calling Windows GDI functions.	Handle property.


Working with pictures

Section topics 	See also

Most of the graphics work you do in Delphi is limited to drawing directly on the canvases of components and forms. Delphi also provides for handling standalone graphic images, such as bitmaps, metafiles, and icons, including automatic management of palettes.

There are three important aspects to working with pictures in Delphi:

Using a picture, graphic, or canvas

Loading and storing graphics

Handling palettes


Using a picture, graphic, or canvas

Section topics 	See also

There are three kinds of classes in Delphi that deal with graphics:

A canvas represents a bitmapped drawing surface on a form, graphic control, printer, or bitmap. A canvas is always a property of something else, never a standalone class.

A graphic represents a graphic image of the sort usually found in a file or resource, such as a bitmap, icon, or metafile. Delphi defines classes TBitmap, TIcon, and TMetafile, all descended from a generic TGraphic. You can also define your own graphic classes. By defining a minimal standard interface for all graphics, TGraphic provides a simple mechanism for applications to use different kinds of graphics easily.

A picture is a container for a graphic, meaning it could contain any of the graphic classes. That is, an item of type TPicture can contain a bitmap, an icon, a metafile, or a user-defined graphic type, and an application can access them all in the same way through the picture class. For example, the image control has a property called Picture, of type TPicture, enabling the control to display images from many kinds of graphics.

Keep in mind that a picture class always has a graphic, and a graphic might have a canvas. (The only standard graphic that has a canvas is TBitmap.) Normally, when dealing with a picture, you work only with the parts of the graphic class exposed through TPicture. If you need access to the specifics of the graphic class itself, you can refer to the picture's Graphic property.


Loading and storing graphics

Section topics 	See also	Example

All pictures and graphics in Delphi can load their images from files and store them back again (or into different files). You can load or store the image of a picture at any time.

To load an image into a picture from a file, call the picture's LoadFromFile method.

To save an image from a picture into a file, call the picture's SaveToFile method.

LoadFromFile and SaveToFile each take the name of a file as the only parameter. LoadFromFile uses the extension of the file name to determine what kind of graphic object it will create and load. SaveToFile saves whatever type of file is appropriate for the type of graphic object being saved.


An example of loading a bitmap

Section topics

To load a bitmap into an image control's picture pass the name of a bitmap file to the picture's LoadFromFile method:

procedure TForm1.LoadBitmapClick(Sender: TObject);
begin
  Image1.Picture.LoadFromFile('RANDOM.BMP');
end;

The picture recognizes .BMP as the standard extension for bitmap files, so it creates its graphic as a TBitmap, then calls that graphic's LoadFromFile method. Because the graphic is a bitmap, it loads the image from the file as a bitmap.


Handling palettes

Section topics 	See also

When running on a palette-based device, Delphi controls automatically support palette realization. That is, if you have a control that has a palette, you can use two methods inherited from TControl to control how Windows accommodates that palette.

Most controls have no need for a palette, but controls that contain graphic images (such as the image control) might need to interact with Windows and the screen device driver to ensure the proper appearance of the control. Windows refers to this process as realizing palettes.

Realizing palettes is the process of ensuring that the frontmost window uses its full palette, and that windows in the background use as much of their palettes as possible, then map any other colors to the closest available colors in the "real" palette. As windows move in front of one another, Windows continually realizes the palettes.

Note:	Delphi itself provides no specific support for creating or maintaining palettes, other than in bitmaps. If you have a palette handle, however, Delphi controls can manage it for you.


Specifying a palette for a control

Section topics 	See also

To specify a palette for a control, override the control's GetPalette method to return the handle of the palette.

Specifying the palette for a control does these things for your application:

It tells the application that your control's palette needs to be realized.

It designates the palette to use for realization.


Responding to palette changes

Section topics 	See also

If your control specifies a palette by overriding GetPalette, Delphi automatically takes care of responding to palette messages from Windows. The method that handles the palette messages is PaletteChanged.

The primary role of PaletteChanged is to determine whether to realize the control's palette in the foreground or the background. Windows handles this realization of palettes by making the topmost window have a foreground palette, with other windows resolved in background palettes. Delphi goes one step further, in that it also realizes palettes for controls within a window in tab order. The only time you might need to override this default behavior is if you want a control that is not first in tab order to have the foreground palette.


Offscreen bitmaps

Section topics 	See also

When drawing complex graphic images, a common technique in Windows programming is to create an offscreen bitmap, draw the image on the bitmap, and then copy the complete image from the bitmap to the final destination onscreen. Using an offscreen image reduces flicker caused by repeated drawing directly to the screen.

The bitmap class in Delphi, which represents bitmapped images in resources and files, can also work as an offscreen image.


Creating and managing offscreen bitmaps

Section topics 	See also

When creating complex graphic images, you should avoid drawing them directly on a canvas that appears onscreen. Instead of drawing on the canvas for a form or control, you can construct a bitmap object, draw on its canvas, and then copy its completed image to the onscreen canvas.

The most common use of an offscreen bitmap is in the Paint method of a graphic control. As with any temporary object, the bitmap should be protected with a try..finally block:

type
  TFancyControl = class(TGraphicControl)
  protected
    procedure Paint; override;	{ override the Paint method }
  end;

procedure TFancyControl.Paint;
var
  Bitmap: TBitmap;	{ temporary variable for the offscreen bitmap }
begin
  Bitmap := TBitmap.Create;	{ construct the bitmap object }

 try
    { draw on the bitmap }

   { copy the result into the control's canvas }

 finally
    Bitmap.Free;	{ destroy the bitmap object }
  end;
end;


Copying bitmapped images

Section topics 	See also

Delphi provides four different ways to copy images from one canvas to another. Depending on the effect you want to create, you call different methods.

The following table summarizes the image-copying methods in canvas objects.

To create this effect	Call this method
Copy an entire graphic.	Draw
Copy and resize a graphic.	StretchDraw
Copy part of a canvas.	CopyRect
Copy a bitmap with raster operations.	BrushCopy


Responding to changes

Section topics 	See also	Example

All graphic objects, including canvases and their owned objects (pens, brushes, and fonts) have events built into them for responding to changes in the object. By using these events, you can make your components (or the applications that use them) respond to changes by redrawing their images.

Responding to changes in graphic objects is particularly important if you publish them as part of the design-time interface of your components. The only way to ensure that the design-time appearance of the component matches the properties set in the Object Inspector is to respond to changes in the objects.

To respond to changes in a graphic object, assign a method to the class's OnChange event.


An example of responding to changes

Section topics

The shape component publishes properties representing the pen and brush it uses to draw its shape. The component's constructor assigns a method to the OnChange event of each, causing the component to refresh its image if either the pen or brush changes:

type
  TShape = class(TGraphicControl)
  public
    procedure StyleChanged(Sender: TObject);
  end;

...

implementation

...

constructor TShape.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);	{ always call the inherited constructor! }

 Width := 65;

 Height := 65;

 FPen := TPen.Create;	{ construct the pen }

 FPen.OnChange := StyleChanged;	{ assign method to OnChange event }

 FBrush := TBrush.Create;	{ construct the brush }

 FBrush.OnChange := StyleChanged;	{ assign method to OnChange event }
end;

procedure TShape.StyleChanged(Sender: TObject);
begin
  Invalidate();	 { erase and repaint the component }
end;


Handling messages

Section topics	See also

One of the keys to traditional Windows programming is handling the messages sent by Windows to applications. Delphi handles most of the common ones for you. It's possible, however, that you will need to handle messages that Delphi doesn't already handle or that you will create your own messages.

There are three aspects to working with messages:

Understanding the message-handling system

Changing message handling

Creating new message handlers


Understanding the message-handling system

Section topics	See also

All Delphiclasses have a built-in mechanism for handling messages, called message-handling methods or message handlers. The basic idea of message handlers is that the class receives messages of some sort and dispatches them, calling one of a set of specified methods depending on the message received. If no specific method exists for a particular message, there is a default handler.

The following diagram shows the message-dispatch system:

The Visual Component Library defines a message-dispatching system that translates all Windows messages (including user-defined messages) directed to a particular class into method calls. You should never need to alter this message-dispatch mechanism. All you'll need to do is create message-handling methods.


What's in a Windows message?

Section topics	See also

A Windows message is a data record that contains several fields. The most important of these is an integer-size value that identifies the message. Windows defines many messages, and the Messages unit declares identifiers for all of them. Other useful information in a message comes in two parameter fields and a result field.

One parameter contains 16 bits, the other 32 bits. You often see Windows code that refers to those values as wParam and lParam, for "word parameter" and "long parameter." Often, each parameter will contain more than one piece of information, and you see references to names such as lParamHi, which refers to the high-order word in the long parameter.

Originally, Windows programmers had to remember or look up in the Windows API what each parameter contained. More recently, Microsoft has named the parameters. This so-called "message cracking" makes it much simpler to understand what information accompanies each message. For example, the parameters to the WM_KEYDOWN message are now called nVirtKey and lKeyData, which gives much more specific information than wParam and lParam.

For each type of message, Delphi defines a record type that gives a mnemonic name to each parameter. For example, mouse messages pass the x- and y-coordinates of the mouse event in the long parameter, one in the high-order word, and the other in the low-order word. Using the mouse-message structure, you don't have to worry about which word is which, because you refer to the parameters by the names XPos and YPos instead of lParamLo and lParamHi.


Dispatching messages

Section topics	See also

When an application creates a window, it registers a window procedure with the Windows kernel. The window procedure is the routine that handles messages for the window. Traditionally, the window procedure contains a huge case statement with entries for each message the window has to handle. Keep in mind that "window" in this sense means just about anything on the screen: each window, each control, and so on. Every time you create a new type of window, you have to create a complete window procedure.

Delphi simplifies message dispatching in several ways:

Each component inherits a complete message-dispatching system.

The dispatch system has default handling. You define handlers only for messages you need to respond to specially.

You can modify small parts of the message-handling and rely on inherited methods for most processing.

The greatest benefit of this message dispatch system is that you can safely send any message to any component at any time. If the component doesn't have a handler defined for the message, the default handling takes care of it, usually by ignoring the message.

Tracing the flow of messages

Delphi registers a method called MainWndProc as the window procedure for each type of component in an application. MainWndProc contains an exception-handling block, passing the message structure from Windows to a virtual method called WndProc and handling any exceptions by calling the application class's HandleException method.

MainWndProc is a nonvirtual method that contains no special handling for any particular messages. Customizations take place in WndProc, since each component type can override the method to suit its particular needs.

WndProc methods check for any special conditions that affect their processing so they can "trap" unwanted messages. For example, while being dragged, components ignore keyboard events, so the WndProc method of TWinControl passes along keyboard events only if the component is not being dragged. Ultimately, WndProc calls Dispatch, a nonvirtual method inherited from TObject, which determines which method to call to handle the message.

Dispatch uses the Msg field of the message structure to determine how to dispatch a particular message. If the component defines a handler for that particular message, Dispatch calls the method. If the component doesn't define a handler for that message, Dispatch calls DefaultHandler.


Changing message handling

Section topics	See also

Before changing the message-handling of your components, make sure that's what you really want to do. Delphi translates most Windows messages into events that both the component writer and the component user can handle. Rather than changing the message-handling behavior, you should probably change the event-handling behavior.

To change message handling, you override the message-handling method. You can also prevent a component from handling a message under certain circumstances by trapping the message.


Overriding the handler method

Section topics	See also	Example

To change the way a component handles a particular message, you override the message-handling method for that message. If the component doesn't already handle the particular message, you need to declare a new message-handling method.

To override a message-handling method, you declare a new method in your component with the same message index as the method it overrides. Do not use the override directive; you must use the message directive and a matching message index.

Note that the name of the method and the type of the single var parameter do not have to match the overridden method. Only the message index is significant. For clarity, however, it is best to follow the convention of naming message-handling methods after the messages they handle.


An example of overriding a message handler

Section topics

For example, to override a component's handling of the WM_PAINT message, you redeclare the WMPaint method:

type

 TMyComponent = class(...)

 ...

 procedure WMPaint(var Message: TWMPaint); message WM_PAINT;

end;


Using message parameters

Section topics	See also	Example

Once inside a message-handling method, your component has access to all the parameters of the message structure. Because the parameter passed to the message handler is a var parameter, the handler can change the values of the parameters if necessary. The only parameter that changes frequently is the Result field for the message: the value returned by the SendMessage call that sends the message.

Because the type of the Message parameter in the message-handling method varies with the message being handled, you should refer to the documentation on Windows messages for the names and meanings of individual parameters. If for some reason you need to refer to the message parameters by their old-style names (WParam, LParam, and so on), you can typecast Message to the generic type TMessage, which uses those parameter names.


Trapping messages

Section topics	See also	Example

Under some circumstances, you might want your components to ignore messages. That is, you want to keep the component from dispatching the message to its handler. To trap a message, you override the virtual method WndProc.

The WndProc method screens messages before passing them to the Dispatch method, which in turn determines which method gets to handle the message. By overriding WndProc, your component gets a chance to filter out messages before dispatching them. An override of WndProc for a control derived from TWinControl looks like this:

procedure TMyControl.WndProc(var Message: TMessage);

begin

 { tests to determine whether to continue processing }

 inherited WndProc(Message);

end;

The TControl component defines entire ranges of mouse messages that it filters when a user is dragging and dropping controls. Overriding WndProc helps this in two ways:

It can filter ranges of messages instead of having to specify handlers for each one.

It can preclude dispatching the message at all, so the handlers are never called.


TheWndProc method

Section topics

Here is part of the WndProc method for TControl, for example:

procedure TControl.WndProc(var Message: TMessage);
begin
  ...

 if (Message.Msg >= WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST) then

   if Dragging then                                           { handle dragging specially }

     DragMouseMsg(TWMMouse(Message))

   else

     ...                                                           { handle others normally }

   end;

...                                                             { otherwise process normally }

end;


Creating new message handlers

Section topics	See also

Because Delphi provides handlers for most common Windows messages, the time you will most likely need to create new message handlers is when you define your own messages. Working with user-defined messages has two aspects:

Defining your own messages

Declaring a new message-handling method


Defining your own messages

Section topics	See also

A number of the standard components define messages for internal use. The most common reasons for defining messages are broadcasting information not covered by standard Windows messages and notification of state changes.

Defining a message is a two-step process. The steps are

1.	Declaring a message identifier.

2.	Declaring a message-record type.


Declaring a message identifier

Section topics	See also	Example

A message identifier is an integer-sized constant. Windows reserves the messages below 1,024 for its own use, so when you declare your own messages you should start above that level.

The constant WM_USER represents the starting number for user-defined messages. When defining message identifiers, you should base them on WM_USER.

Be aware that some standard Windows controls use messages in the user-defined range. These include list boxes, combo boxes, edit boxes, and command buttons. If you derive a component from one of these and want to define a new message for it, be sure to check the Messages unit to see which messages Windows already defines for that control.


An example of user-defined messages

Section topics

The following code shows two user-defined messages:

const

 WM_MYFIRSTMESSAGE = WM_USER + 400;

 WM_MYSECONDMESSAGE = WM_USER + 401;


Declaring a message-record type

Section topics	See also	Example

If you want to give useful names to the parameters of your message, you need to declare a message- record type for that message. The message record is the type of the parameter passed to the message-handling method. If you don't use the message's parameters, or if you want to use the old-style parameter notation (wParam, lParam, and so on), you can use the default message record, TMessage.

To declare a message-record type, follow these conventions:

1.	Name the type after the message, preceded by a T.

2.	Call the first field in the record Msg, of type TMsgParam.

3.	Define the next two bytes to correspond to the Word parameter, and the next two bytes as unused.

Or

Define the next four bytes to correspond to Longint parameter.

4.	Add a final field called Result, of type Longint.

For example, here is the message record for all mouse messages, TWMMouse, which uses a variant record to define two sets of names for the same parameters.

type

 TWMMouse = record

   Msg: TMsgParam; ( first is the message ID )

   Keys: Word; ( this is the wParam )

   case Integer of ( two ways to look at the lParam )

     0: {

        XPos: Integer; ( either as x- and y-coordinates...)

        YPos: Integer);

     1: {

        Pos: TPoint; ( ... or as a single point )

        Result: Longint); ( and finally, the result field )

end;


Declaring a new message-handling method

Section topics	See also	Example

There are two sets of circumstances that require you to declare new message-handling methods:

Your component needs to handle a Windows message that isn't already handled by the standard components.

You have defined your own message for use by your components.

To declare a message-handling method, do the following:

1.	Declare the method in a protected part of the component's class declaration.

2.	Make the method a procedure.

3.	Name the method after the message it handles, but without any underline characters.

4.	Pass a single var parameter called Message, of the type of the message record.

5.	Within the message method implementation, write code for any handling specific to the component.

6.	Call the inherited message handler.


An example of a message handler

Section topics

Here's the declaration of a message handler for a user-defined message called CM_CHANGECOLOR:

const

 CM_CHANGECOLOR = WM_USER + 400;

type

 TMyComponent = class(TControl)

 ...

protected

 procedure CMChangeColor(var Message: TMessage): message CM_CHANGECOLOR;

end;

procedure TMyComponent.CMChangeColor(var Message: TMessage);

begin

 Color := Message.lParam;

 inherited;

end;


Registering components

Section topics	See also

Registering a component is a prerequisite to installing it in the IDE. Once a component is installed, you can select it from the Component palette, place it on a form, and manipulate it at design time.

Making your components available at design time requires several steps:

Registering components

Adding palette bitmaps

Providing Help for your component

Storing and loading properties

These steps don't apply to every component. For example, if you don't define any new properties or events, you don't need to provide Help for them. The only step that is always necessary is registration.


Registering components

Section topics	See also

Registration works on a unit basis, so if you create several components in a single unit, you register them all at once.

To register a component, add a Register procedure to the component unit. This has two parts:

Declaring the Register procedure

Implementing the Register procedure

Once you've set up the registration, you can install the components onto the Component palette as described in the User's Guide.


Declaring the Register procedure

Section topics

Registration involves writing a single procedure in the component unit, which must have the name Register. Register must appear in the interface part of the unit. Within the Register procedure, you call the RegisterComponents for each component you want to register.

The following code shows the outline of a simple unit that creates and registers new components.

unit MyBtns;

interface

type

 { declare your component types here }

procedure Register; { this must appear in the interface section }

implementation

 { component implementation goes here }

procedure Register;
begin
  { register the components }
end;
end.


Implementing the Register procedure

Section topics

Inside the Register procedure for a unit containing components, you must register each component you want to add to the Component palette. If the unit contains several components, you can register them all in one step.

To register a component, call the RegisterComponents procedure once for each page of the Component palette to which you want to add components. RegisterComponents tells Delphi two important things:

The names of the components to install.

The name of the palette page you want to install them on.

The palette-page name is a string. If the name you give for the palette page doesn't already exist, Delphi creates a new page with that name. Delphi stores the names of the standard pages in string-list resources so that international versions of the product can name the pages in their native languages. If you want to install a component on one of the standard pages, you should obtain the string for the page name by calling the LoadStr function, passing the constant representing the string resource for that page, such as srSystem for the System page.

You pass the component names in an open array, which you can construct inside the call to RegisterComponents.

The following Register procedure, for example, registers a component named TMyComponent and places it on a Component palette page called "Miscellaneous."

procedure Register;
begin
  RegisterComponents('Miscellaneous', [TMyComponent]);
end;

You could also register several components on the same page at once, or register components on different pages, as shown in the following code:

procedure Register;
begin
  RegisterComponents('Miscellaneous', [TFirst, TSecond]); { two on this page... }

 RegisterComponents('Assorted', [TThird]); { ...one on another... }

 RegisterComponents(LoadStr(srStandard), [TFourth]); and one on the Standard page }

end;


Adding palette bitmaps

Section topics	See also	Example

Every component needs a bitmap to represent the component on the Component palette. If you don't specify your own bitmap, Delphi uses a default bitmap.

Because the palette bitmaps are needed only at design time, you don't compile them into the component's unit. Instead, you supply them in a Windows resource file with the same name as the unit, but with the extension .DCR (dynamic component resource). You can create this resource file using the  in Delphi. Each bitmap should be 24 pixels square.

For each component you want to install, supply a palette bitmap file, and within each palette bitmap file, supply a bitmap for each component you register. The bitmap image has the same name as the component. Keep the palette bitmap file in the same directory with the compiled unit, so Delphi can find the bitmaps when it installs the components on the Component palette.


An example of adding a palette bitmap

Section topics

For example, if you create a component named TMyControl in a unit named ToolBox, you need to create a resource file called TOOLBOX.DCR that contains a bitmap called TMYCONTROL. The resource names are not case-sensitive, but by convention they are usually in uppercase letters.


Providing Help for your component

Section topics

When you select a standard component on a form, or a property or event in the Object Inspector, you can press F1 to get Help on that item. Developers can get the same kind of documentation for your components if you create the appropriate Help files.

You can provide a small Help file with just the information on your components, and users will be able to find your documentation without having to take any special steps. Your help file becomes part of the user's overall Delphi Help system.


Creating the Help file

Section topics

You can use any tools you want to create a Windows Help file. Delphi includes the Microsoft Help Workshop, which compiles your Help files and provides an online help authoring guide. You can find complete information about creating Help files in the online guide.

To make your component's Help work with the Help for the rest of the components in the library, observe the following conventions:

1.	Each component should have a screen.

The component screen should show which unit it's declared in, give a brief description of the component's purpose, then list separately all the properties, events, and methods available to component users. Application developers access this screen by selecting the component on a form and pressing F1. For an example of a component screen, place any component on a form and press F1.

The component screen should have a "K" footnote for keyword searching that includes the name of the component. For example, the keyword footnote for the TMemo component reads "TMemo."

2.	Each property, event, and method that is declared within the component should have a screen.

A property, event, or method screen should show the declaration of the item and describe its use. Application developers see these screens either by highlighting the item in the Object Inspector and pressing F1 or by placing the cursor in the Code editor on the name of the item and pressing F1. To see an example of a property screen, select any item in the Object Inspector and press F1.

Each component, property, event, or method screen should have

A topic ID that is unique to the topic, entered as a "#" footnote.

A title entered as a "$" footnote.

The title appears as specified in the Topics Found dialog box, the Bookmark dialog box, and the History window.

A "K" footnote for keyword searching that includes the name of the item.

For example, the keyword footnote for the Top property reads "Top". For TMemo, the keyword reads "TMemo".


Providing context sensitivity for your component
Providing context-sensitive help for your component

Section topics	See also

To provide context-sensitive help for a Delphi component, you need to

Insert “A” footnotes into your RTF source file.

Add a Link statement to the DELPHI3.CFG file.

Add an Include statement to your CNT file.

The A footnotes determine which screen appears when the user selects a component or property and presses F1. Each component, property, and event screen must have an A footnote that follows the conventions described below.

A footnote conventions

If the Help screen is for a component, the A footnote consists of two entries separated by a semicolon using this syntax:

ComponentName_Object;ComponentName

where ComponentName is the name of the component.

For example, for a component named TFileDialog, the A footnote is

TFileDialog_Object;TFileDialog

If the Help screen is for a property or event, the A footnote consists of three entries separated by semicolons using this syntax:

ComponentName_Propertyname;Propertyname_Property;Propertyname

or

ComponentName_Eventname;Eventname_Event;Eventname

where ComponentName is the name of the component and Propertyname or Eventname is the name of the property or event.

For example, for a property named BackgroundColor of a component named MyGrid, the A footnote is

MyGrid_BackgroundColor;BackgroundColor_Property;BackgroundColor

How to connect your help file and CNT to DELPHI3.CFG

To connect your context-sensitive Help to the Delphi IDE, you need to do two more things:

Add a Link statement referencing the help file to the "Third-Party Help" section of DELPHI3.CFG.

Example: :Link 3RDPARTY.HLP

Add an Include statement to your help file’s CNT that points to DELPHI3.CFG.

Example: :Include DELPHI3.CFG


Storing and loading properties

Section topics	See also

Delphi stores forms and their components in form (.DFM) files. A form file is a binary representation of the properties of a form and its components. When Delphi developers add the components you write to their forms, your components must have the ability to write their properties to the form file when saved. Similarly, when loaded into Delphi or executed as part of an application, the components must restore themselves from the form file.

Most of the time you won't need to do anything to make your components work with form files because the ability to store a representation and load from it are part of the inherited behavior of components. Sometimes, however, you might want to alter the way a component stores itself or the way it initializes when loaded; so you should understand the underlying mechanism.

These are the aspects of property storage you need to understand:

Using the store-and-load mechanism

Specifying default values

Determining what to store

Initializing after loading


Using the store-and-load mechanism

Section topics	See also

The description of a form consists of a list of the form's properties, along with similar descriptions of each component on the form. Each component, including the form itself, is responsible for storing and loading its own description.

By default, when storing itself, a component writes the values of all its public and published properties that differ from their default values, in the order of their declaration. When loading itself, a component first constructs itself, setting all properties to their default values, then reads the stored, non-default property values.

This default mechanism serves the needs of most components, and requires no action at all on the part of the component writer. There are several ways you can customize the storing and loading process to suit the needs of your particular components, however.


Specifying default values

Section topics	See also	Example

Delphi components save their property values only if those values differ from the defaults. If you don't specify otherwise, Delphi assumes a property has no default value, meaning the component always stores the property, whatever its value.

To specify a default value for a property, add the default directive and the new default value to the end of the property declaration.

You can also specify a default value when redeclaring a property. In fact, one reason to redeclare a property is to designate a different default value.

Note:	Specifying the default value does not automatically assign that value to the property on creation of the object. You must make sure that the component's constructor assigns the necessary value. A property whose value is not set by a component's constructor assumes a zero value--that is, whatever value the property assumes when its storage memory is set to 0. Thus numeric values default to 0, Boolean values to False, pointers to nil, and so on. If there is any doubt, assign a value in the constructor method.


An example of specifying a default value

Section topics

The following code shows a component declaration that specifies a default value for the Align property and the implementation of the component's constructor that sets the default value. In this case, the new component is a special case of the standard panel component that will be used for status bars in a window, so its default alignment should be to the bottom of its owner.

type
  TStatusBar = class(TPanel)
  public
    constructor Create(AOwner: TComponent); override; { override to set new default }

 published
    property Align default alBottom; { redeclare with new default value }

 end;

...

constructor TStatusBar.Create(AOwner: TComponent);
begin
  inherited Create(AOwner); { perform inherited initialization }

 Align := alBottom; { assign new default value for Align }

end;


Determining what to store

Section topics	See also	Example

You can control whether Delphi stores each of your components' properties. By default, all properties in the published part of the class declaration are stored. You can choose not to store a given property at all, or you can designate a function that determines at runtime whether to store the property.

To control whether Delphi stores a property, add the stored directive to the property declaration, followed by True, False, or the name of a Boolean method.


An example of stored properties

Section topics

The following code shows a component that declares three new properties. One is always stored, one is never stored, and the third is stored depending on the value of a Boolean method:

type
  TSampleComponent = class(TComponent)

 protected
    function StoreIt: Boolean;

 public { normally not stored }
    property Important: Integer stored True;	{ always stored }

 published { normally stored always }
    property Unimportant: Integer stored False;	{ never stored }
    property Sometimes: Integer stored StoreIt;	{ storage depends on function value }
  end;


Initializing after loading

Section topics	See also

After a component reads all its property values from its stored description, it calls a virtual method named Loaded, which performs any required initializations. The call to Loaded occurs before the form and its controls are shown, so you don't need to worry about initialization causing flicker on the screen.

To initialize a component after it loads its property values, override the Loaded method.

Note:	The first thing to do in any Loaded method is call the inherited Loaded method. This ensures that any inherited properties are correctly initialized before you initialize your own component.

The following code comes from the TDatabase component. After loading, the database tries to reestablish any connections that were open at the time it was stored, and specifies how to handle any exceptions that occur while connecting.

procedure TDatabase.Loaded;

begin

 inherited Loaded; { call the inherited method first}

 try

   if FStreamedConnected then Open { reestablish connections }

   else CheckSessionName(False);

 except

   if csDesigning in ComponentState then { at design time... }

     Application.HandleException(Self)   { let Delphi handle the exception }

   else raise; { otherwise, reraise }

 end;

end;


Creating packages

Section topics 	See also

To install your components in the IDE, you must first compile them as packages. A package is a special dynamic-link library used by Delphi applications, the Delphi IDE, or both. A package can contain one or several components as well as custom property editors. To distinguish them from other DLLs, package libraries are stored in files that end with the .DPL (Delphi package library) extension.

Creating a package involves:

Writing unit files that contain the source code for your components.

Editing a package source file that contains these unit files.

Ensuring that your package doesn't include circular references to other packages or redundant source code uses.

Compiling the package into a DPL.

Generating a package collection, if desired, for easier distribution of your components to other developers.


Runtime and design-time packages

Section topics 	See also

You can compile Delphi component packages for use at runtime or design-time. Runtime packages provide functionality when a user runs an application. Design-time packages are used to install components on the Component palette and to create special property editors for custom components.

To install components on the Component palette, you must compile them as design-time packages. The DCLUSR30 package is provided as a default container for new components.

Runtime packages are optional, but there are good reasons to use them. By compiling your components into a runtime package, you can share them among applications. Since the applications don't have separate copies of the components bound into their executables, the executables are much smaller, saving both system resources and hard disk storage. Moreover, packages allow faster compilation because only code unique to the application is compiled with each build.

A single package can function at both design time and runtime, and design-time packages sometimes work by calling runtime packages. Because design-time packages contain extra code required by the IDE, it is usually best to compile separate runtime and design-time version of each component package.


See also

Using packages


Modifying an existing component

Section topics 	See also

The easiest way to create a component is to derive it from a component that does nearly everything you want, then make whatever changes you need. What follows is a simple example that modifies the standard memo component to create a memo that does not wrap words by default.

The value of the memo component's WordWrap property is initialized to True. If you frequently use non-wrapping memos, you can create a new memo component that doesn't wrap words by default.

Note:	To modify published properties or save specific event handlers for an existing component, it is often easier to use a component template.

Modifying an existing component takes only two steps:

Creating and registering the component

Modifying the component class


Creating and registering the component

Section topics 	See also

Creation of every component begins the same way: you create a unit, derive a component class, register it, and install it on the Component palette.

For this example, follow the general procedure for creating a component, with these specifics:

Call the component's unit Memos.

Derive a new component type called TWrapMemo, descended from TMemo.

Register TWrapMemo on the Samples page of the Component palette.

The resulting unit should look like this:

unit Memos;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, StdCtrls;

type
  TWrapMemo = class(TMemo)
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Samples', [TWrapMemo]);
end;

end.

If you compile and install the new component now, it behaves exactly like its ancestor, TMemo. In the next section, you'll make a simple change to your component.


Modifying the component class

Section topics 	See also

Once you've created a new component class, you can modify it in almost any way. In this case, you will change only the initial value of one property in the memo component. This involves two small changes to the component class:

Overriding the constructor

Specifying the new default property value

The constructor actually sets the value of the property. The default tells Delphi what values to store in the form (.DFM) file. Delphi stores only values that differ from the default, so it is important to perform both steps.


Overriding the constructor

Section topics 	See also

All components set their initial property values when constructed. When a component is placed on a form at design time, or when an application constructs a component at runtime, the component's constructor sets the property values. When a component is loaded from a form file, the application sets any properties changed at design time.

Note:	When you override a constructor, the new constructor must call the inherited constructor before doing anything else.For this example, your new component needs to override the constructor inherited from TMemo to set the WordWrap property to False. To achieve this, add the constructor override to the forward declaration, then write the new constructor in the implementation part of the unit:

type
  TWrapMemo = class(TMemo)

 public { constructors are always public }
    constructor Create(AOwner: TComponent); override; { this syntax is always the same }

 end;

...

constructor TWrapMemo.Create(AOwner: TComponent); { this goes after implementation }
begin
  inherited Create(AOwner); { ALWAYS do this first! }
  WordWrap := False; { set the new desired value }
end;

Now you can install the new component on the Component palette and add it to a form. Note that the WordWrap property is now initialized to False.

If you change an initial property value, you should also designate that value as the default. If you fail to match the value set by the constructor to the specified default value, Delphi cannot store and restore the proper value.


Specifying the new default property value

Section topics 	See also

When Delphi stores a description of a form in a form file, it stores the values only of properties that differ from their defaults. Storing only the differing values keeps the form files small and makes loading the form faster. If you create a property or change the default value, it's a good idea to update the property declaration to include the new default. Form files, loading, and default values are explained in more detail in "Registering components."

To change the default value of a property, redeclare the property name, followed by the directive default and the new default value. You don't need to redeclare the entire property, just the name and the default value.

For the word-wrapping memo component, you redeclare the WordWrap property in a published part of the object declaration, with a default value of False:

type
  TWrapMemo = class(TMemo)

 ...

 published
    property WordWrap default False;
  end;

Specifying the default property value doesn't affect the workings of the component. You must still initialize the value in the component's constructor. Redeclaring the default ensures that Delphi knows when to write WordWrap to the form file.


See also

Registering components

TMemo.


Creating a graphic component

Section topics 	See also

A graphic control is a simple kind of component. Because a purely graphic control never receives focus, it doesn't have or need a window handle. Users can still manipulate the control with the mouse, but there is no keyboard interface.

The graphic component presented in these topics is TShape, the shape component on the Additional page of the Component palette. Although the component created is identical to the standard shape component, you need to call it something different to avoid duplicate identifiers. These topics call their shape component TSampleShape and shows you all the steps involved in creating the shape component:

1.	Creating and registering the component

2.	Publishing inherited properties

3.	Adding graphic capabilities


Creating and registering the component

Section topics 	See also

Creation of every component begins the same way: You create a unit, derive a component class, register it, compile it, and install it on the Component palette.

For this example, follow the general procedure for creating a component, with these specifics:

Call the component's unit Shapes.

Derive a new component type called TSampleShape, descended from TGraphicControl.

Register TSampleShape on the Samples page of the Component palette.

The resulting unit should look like this:

unit Shapes;

interface

uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms;

type

 TSampleShape = class(TGraphicControl)

 end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponent('Samples', [TSampleShape]);
end;

end.


Publishing inherited properties

Section topics 	See also

Once you derive a component type, you can decide which of the properties and events declared in the protected parts of the ancestor class you want to surface in the new component. TGraphicControl already publishes all the properties that enable the component to function as a control, so all you need to publish is the ability to respond to mouse events and handle drag-and-drop.

Publishing inherited properties and events is explained in "Publishing inherited properties" and "Making events visible." Both processes involve redeclaring just the name of the properties in the published part of the class declaration.

For the shape control, you can publish the three mouse events, the three drag-and-drop events, and the two drag-and-drop properties:

type
  TSampleShape = class(TGraphicControl)

 published
    property DragCursor; { drag-and-drop properties }

   property DragMode;

   property OnDragDrop; { drag-and-drop events }

   property OnDragOver;

   property OnEndDrag;

   property OnMouseDown; { mouse events }

   property OnMouseMove;

   property OnMouseUp;

 end;

The sample shape control now makes mouse and drag-and-drop interactions available to its users.


Adding graphic capabilities

Section topics 	See also

Once you have declared your graphic component and published any inherited properties you want to make available, you can add the graphic capabilities that distinguish your component.

1.	Determining what to draw

2.	Drawing the component image

In addition, for the shape control example, you'll add some properties that enable application developers to customize the appearance of the shape at design time.


Determining what to draw

Section topics 	See also

A graphic control can change its appearance to reflect a dynamic condition, including user input. A graphic control that always looks the same should probably not be a component at all. If you want a static image, you can import the image instead of using a control.

In general, the appearance of a graphic control depends on some combination of its properties. The gauge control, for example, has properties that determine its shape and orientation and whether it shows its progress numerically as well as graphically. Similarly, the shape control has a property that determines what kind of shape it should draw.

To give your control a property that determines the shape it draws, add a property called Shape. This requires:

1.	Declaring the property type

2.	Declaring the property

3.	Writing the implementation method

Creating properties is explained in more detail in "Creating properties."

Declaring the property type

When you declare a property of a user-defined type, you must declare the type first, before the class that includes the property. The most common sort of user-defined type for properties is enumerated.

For the shape control, you need an enumerated type with an element for each kind of shape the control can draw.

Add the following type definition above the shape control class's declaration.

type
  TSampleShapeType = (sstRectangle, sstSquare, sstRoundRect, sstRoundSquare,
    sstEllipse, sstCircle);
  TSampleShape = class(TGraphicControl)	{ this is already there }

You can now use this type to declare a new property in the class.

Declaring the property

When you declare a property, you usually need to declare a private field to store the data for the property, then specify methods for reading and writing the property value. Often, you don't need to use a method to read the value, but can just point to the stored data instead.

For the shape control, you'll declare a field that holds the current shape, then declare a property that reads that field and writes to it through a method call.

Add the following declarations to TSampleShape:

type
  TSampleShape = class(TGraphicControl)

 private
    FShape: TSampleShapeType;	{ field to hold property value }
    procedure SetShape(Value: TSampleShapeType);

 published
    property Shape: TSampleShapeType read FShape write SetShape;
  end;

Now all that remains is to add the implementation of SetShape.

Writing the implementation method

When the read or write part of a property definition uses a method instead of directly accessing the stored property data, you need to implement the method.

Add the implementation of the SetShape method to the implementation part of the unit:

procedure TSampleShape.SetShape(Value: TSampleShapeType);
begin
  if FShape <> Value then	{ ignore if this isn't a change }

 begin
    FShape := Value; { store the new value }

   Invalidate; { force a repaint with the new shape }
  end;
end;


Overriding the constructor and destructor

Section topics 	See also

To change default property values and initialize owned classes for your component, you must override the inherited constructor and destructor. In both cases, remember always to call the inherited method in your new constructor or destructor.

Changing default property values

The default size of a graphic control is fairly small, so you can change the width and height in the constructor. Changing default property values is explained in more detail in "Modifying an existing component."

In this example, the shape control sets its size to a square 65 pixels on each side.

1.	Add the overridden constructor to the declaration of the component class:

type
  TSampleShape = class(TGraphicControl)

 public { constructors are always public}

   constructor Create(AOwner: TComponent); override { remember override directive }
  end;

2.	Redeclare the Height and Width properties with their new default values:

type
  TSampleShape = class(TGraphicControl)
  ...

 published
    property Height default 65;

   property Width default 65;

 end;

3.	Write the new constructor in the implementation part of the unit:

constructor TSampleShape.Create(AOwner: TComponent);
begin
  inherited Create(AOwner); { always call the inherited constructor }

 Width := 65;

 Height := 65;
end;


Publishing the pen and brush

Section topics 	See also

By default, a canvas has a thin black pen and a solid white brush. To let developers change the pen and brush, you must provide classes for them to manipulate at design time, then copy the classes into the canvas during painting. Classes such as an auxiliary pen or brush are called owned classes because the component owns them and is responsible for creating and destroying them.

Managing owned classes requires:

1.	Declaring the class fields.

2.	Declaring the access properties.

3.	Initializing owned classes.

Declaring the class fields

Each class a component owns must have a class field declared for it in the component. The class field ensures that the component always has a pointer to the owned object so that it can destroy the class before destroying itself. In general, a component initializes owned objects in its constructor and destroys them in its destructor.

Fields for owned objects are nearly always declared as private. If applications (or other components) need access to the owned objects, you can declare published or public properties for this purpose.

Add fields for a pen and brush to the shape control:

type
  TSampleShape = class(TGraphicControl)

 private { fields are nearly always private }
    FPen: TPen; { a field for the pen object }

   FBrush: TBrush; { a field for the brush object }

   ...
  end;

Declaring the access properties

You can provide access to the owned objects of a component by declaring properties of the type of the objects. That gives developers a way to access the objects at design time or runtime. Usually, the read part of the property just references the class field, but the write part calls a method that enables the component to react to changes in the owned object.

To the shape control, add properties that provide access to the pen and brush fields. You'll also declare methods for reacting to changes to the pen or brush.

type
  TSampleShape = class(TGraphicControl)
  ...

 private	{ these methods should be private }
    procedure SetBrush(Value: TBrush);
    procedure SetPen(Value: TPen);

 published	{ make these available at design time }
    property Brush: TBrush read FBrush write SetBrush;
    property Pen: TPen read FPen write SetPen;
  end;

Then, write the SetBrush and SetPen methods in the implementation part of the unit:

procedure TSampleShape.SetBrush(Value: TBrush);
begin
  FBrush.Assign(Value); { replace existing brush with parameter }
end;

procedure TSampleShape.SetPen(Value: TPen);
begin
  FPen.Assign(Value); { replace existing pen with parameter }
end;

Initializing owned classes

If you add classes to your component, the component's constructor must initialize them so that the user can interact with the objects at runtime. Similarly, the component's destructor must also destroy the owned objects before destroying the component itself.

Because you've added a pen and a brush to the shape control, you need to initialize them in the shape control's constructor and destroy them in the control's destructor:

1.	Construct the pen and brush in the shape control constructor:

constructor TSampleShape.Create(AOwner: TComponent);
begin
  inherited Create(AOwner); { always call the inherited constructor }

 Width := 65;

 Height := 65;

 FPen := TPen.Create; { construct the pen }

 FBrush := TBrush.Create; { construct the brush }
end;

2.	Add the overridden destructor to the declaration of the component class:

type
  TSampleShape = class(TGraphicControl)

 public { destructors are always public}
    constructor Create(AOwner: TComponent); override;

   destructor Destroy; override; { remember override directive }
  end;

3.	Write the new destructor in the implementation part of the unit:

destructor TSampleShape.Destroy;

begin

 FPen.Free; { destroy the pen object }

 FBrush.Free; { destroy the brush object }

 inherited Destroy; { always call the inherited destructor, too }

end;

Setting owned classes' properties

As the final step in handling the pen and brush classes, you need to make sure that changes in the pen and brush cause the shape control to repaint itself. Both pen and brush classes have OnChange events, so you can create a method in the shape control and point both OnChange events to it.

Add the following method to the shape control, and update the component's constructor to set the pen and brush events to the new method:

type
  TSampleShape = class(TGraphicControl)
  published
    procedure StyleChanged(Sender: TObject);
  end;

...

implementation

...

constructor TSampleShape.Create(AOwner: TComponent);
begin
  inherited Create(AOwner); { always call the inherited constructor }

 Width := 65;

 Height := 65;

 FPen := TPen.Create; { construct the pen }

 FPen.OnChange := StyleChanged; { assign method to OnChange event }

 FBrush := TBrush.Create; { construct the brush }

 FBrush.OnChange := StyleChanged; { assign method to OnChange event }

end;

procedure TSampleShape.StyleChanged(Sender: TObject);
begin
  Invalidate(True); { erase and repaint the component }

end;

With these changes, the component redraws to reflect changes to either the pen or the brush.


Drawing the component image

Section topics 	See also

The essential element of a graphic control is the way it paints its image on the screen. The abstract type TGraphicControl defines a method called Paint that you override to paint the image you want on your control.

The Paint method for the shape control needs to do several things:

Use the pen and brush selected by the user.

Use the selected shape.

Adjust coordinates so that squares and circles use the same width and height.

Overriding the Paint method requires two steps:

1.	Add Paint to the component's declaration.

2.	Write the Paint method in the implementation part of the unit.

For the shape control, add the following declaration to the class declaration:

type
  TSampleShape = class(TGraphicControl)

 ...

 protected
    procedure Paint; override;

 ...
  end;

Then write the method in the implementation part of the unit:

procedure TSampleShape.Paint;
begin
  with Canvas do
  begin
    Pen := FPen; { copy the component's pen }

   Brush := FBrush; { copy the component's brush }

   case FShape of
      sstRectangle, sstSquare:
        Rectangle(0, 0, Width, Height); { draw rectangles and squares }

     sstRoundRect, sstRoundSquare:
        RoundRect(0, 0, Width, Height, Width div 4, Height div 4); { draw rounded shapes }

     sstCircle, sstEllipse:
        Ellipse(0, 0, Width, Height); { draw round shapes }
    end;
  end;
end;

Paint is called whenever the control needs to update its image. Windows tells controls to paint when they first appear or when a window in front of them goes away. In addition, you can force repainting by calling Invalidate, as the StyleChanged method does.


Refining the shape drawing

Section topics 	See also

The standard shape control does one more thing that your sample shape control doesn't yet do: It handles squares and circles as well as rectangles and ellipses. To do that, you need to write code that finds the shortest side and centers the image.

Here's a refined Paint method that adjusts for squares and ellipses:

procedure TSampleShape.Paint;
var
  X, Y, W, H, S: Integer;

begin
  with Canvas do
  begin
    Pen := FPen; { copy the component's pen }

   Brush := FBrush; { copy the component's brush }

   W := Width; { use the component width }

   H := Height; { use the component height }

   if W < H then S := W else S := H; { save smallest for circles/squares }

   case FShape of { adjust height, width and position }

     sstRectangle, sstRoundRect, sstEllipse:
        begin

         X := 0; { origin is top-left for these shapes }

         Y := 0;
        end;

     sstSquare, sstRoundSquare, sstCircle:
        begin
          X := (W - S) div 2; { center these horizontally }
          Y := (H - S) div 2; { and vertically }

         W := S; { use shortest dimension for width }
          H := S; { and for height }
        end;
    end;

   case FShape of
      sstRectangle, sstSquare:
        Rectangle(X, Y, X + W, Y + H); { draw rectangles and squares }

     sstRoundRect, sstRoundSquare:
        RoundRect(X, Y, X + W, Y + H, S div 4, S div 4); { draw rounded shapes }

     sstCircle, sstEllipse:
        Ellipse(X, Y, X + W, Y + H); { draw round shapes }

   end;
  end;
end;


See also

Modifying an existing componentTShapeTGraphicControl


Customizing a grid

Section topics 	See also

Delphi provides abstract components you can use as the basis for customized components. The most important of these are grids and list boxes. In these topics you'll see how to create a small one-month calendar from the basic grid component, TCustomGrid.

Creating the calendar involves these tasks:

Creating and registering the component

Publishing inherited properties

Changing initial values

Resizing the cells

Filling in the cells

Navigating months and years

Navigating days

The resulting component is similar to the TCalendar component on the Samples page of the Component palette.


Creating and registering the component

Section topics 	See also

Creation of every component begins the same way: unit, You create a unit, derive a component class, register it, compile it, and install it on the Component palette.

For this example, follow the general procedure for creating a component, with these specifics:

Call the component's unit CalSamp.

Derive a new component type called TSampleCalendar, descended from TCustomGrid.

Register TSampleCalendar on the Samples page of the Component palette.

The resulting unit should look like this:

unit CalSamp;

interface

uses

 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Grids;

type
  TSampleCalendar = class(TCustomGrid)
  end;

procedure Register;

implementation

procedure Register;

begin
  RegisterComponents('Samples', [TSampleCalendar]);

end;

end.

If you install the calendar component now, you'll find that it appears on the Samples page. The only properties available are the most basic control properties. The next step is to make some of the more specialized properties available to users of the calendar.

Note:	While you can install the sample calendar component you've just compiled, don't try to place it on a form yet. The TCustomGrid component has an abstract DrawCell method that must be redeclared before instance objects can be created. Overriding the DrawCell method is described in "Filling in the cells."


Publishing inherited properties

Section topics 	See also

The abstract grid component, TCustomGrid, provides a large number of protected properties. You can choose which of those properties you want to make available to users of the calendar control.

To make inherited protected properties available to users of your components, redeclare the properties in the published part of your component's declaration.

For the calendar control, publish the following properties and events, as shown here:

type
  TSampleCalendar = class(TCustomGrid)

 published
    property Align; { publish properties }

   property BorderStyle;

   property Color;

   property Ctl3D;

   property Font;

   property GridLineWidth;

   property ParentColor;

   property ParentFont;

   property OnClick; { publish events }

   property OnDblClick;

   property OnDragDrop;

   property OnDragOver;

   property OnEndDrag;

   property OnKeyDown;

   property OnKeyPress;

   property OnKeyUp;
  end;

There are a number of other properties you could also publish, but which do not apply to a calendar, such as the Options property that would enable the user to choose which grid lines to draw.

If you rebuild the Component palette and try out the modified calendar component, you'll find many more properties and events available in the calendar, all fully functional. You can now start adding new capabilities of your own design.


Changing initial values

Section topics 	See also

A calendar is essentially a grid with a fixed number of rows and columns, although not all the rows always contain dates. For this reason, you haven't published the grid properties ColCount and RowCount, because it is highly unlikely that users of the calendar will want to display anything other than seven days per week. You still must set the initial values of those properties so that the week always has seven days, however.

To change the initial values of the component's properties, override the constructor to set the desired values. The constructor must be virtual.

Remember that you need to add the constructor to the public part of the component's object declaration, then write the new constructor in the implementation part of the component's unit. The first statement in the new constructor should always be a call to the inherited constructor.

type
  TSampleCalendar = class(TCustomGrid
  public
    constructor Create(AOwner: TComponent); override;

 ...

 end;
...

constructor TSampleCalendar.Create(AOwner: TComponent);
begin
  inherited Create(AOwner); { call inherited constructor }

 ColCount := 7; { always seven days/week }

 RowCount := 7; { always six weeks plus the headings }

 FixedCols := 0; { no row labels }

 FixedRows := 1; { one row for day names }

 ScrollBars := ssNone; { no need to scroll }

 Options := Options - [goRangeSelect] + [goDrawFocusSelected]; {disable range selection}

end;


Resizing the cells

Section topics 	See also

When a user or application changes the size of a window or control, Windows sends a message called WM_SIZE to the affected window or control so it can adjust any settings needed to later paint its image in the new size. Your component can respond to that message by altering the size of the cells so they all fit inside the boundaries of the control. To respond to the WM_SIZE message, you'll add a message-handling method to the component.

Creating a message-handling method is described in detail in "Creating new message handlers."

In this case, the calendar control needs a response to WM_SIZE, so add a protected method called WMSize to the control indexed to the WM_SIZE message, then write the method so that it calculates the proper cell size to allow all cells to be visible in the new size:

type
  TSampleCalendar = class(TCustomGrid)

 protected
    procedure WMSize(var Message: TWMSize); message WM_SIZE;

 ...

 end;

...

procedure TSampleCalendar.WMSize(var Message: TWMSize);
var
  GridLines: Integer; { temporary local variable }

begin
  GridLines := 6 * GridLineWidth; { calculate combined size of all lines }

 DefaultColWidth := (Message.Width - GridLines) div 7; { set new default cell width }

 DefaultRowHeight := (Message.Height - GridLines) div 7; { and cell height }
end;


Filling in the cells

Section topics 	See also

A grid control fills in its contents cell-by-cell. In the case of the calendar, that means calculating which date, if any, belongs in each cell. The default drawing for grid cells takes place in a virtual method called DrawCell.

To fill in the contents of grid cells, override the DrawCell method.

The easiest part to fill in is the heading cells in the fixed row. The runtime library contains an array with short day names, so for the calendar, use the appropriate one for each column:

type
  TSampleCalendar = class(TCustomGrid)

 protected
    procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
      override;

 end;

...

procedure TSampleCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect;
  AState: TGridDrawState);

begin

 if ARow = 0 then
    Canvas.TextOut(ARect.Left, ARect.Top, ShortDayNames[ACol + 1]); { use RTL strings }

end;


Tracking the date

Section topics 	See also

For the calendar control to be useful, users and applications must have a mechanism for setting the day, month, and year. Delphi stores dates and times in variables of type TDateTime. TDateTime is an encoded numeric representation of the date and time, which is useful for programmatic manipulation, but not convenient for human use.

You can therefore store the date in encoded form, providing runtime access to that value, but also provide Day, Month, and Year properties that users of the calendar component can set at design time.

Storing the internal date

To store the date for the calendar, you need a private field to hold the date and a runtime-only property that provides access to that date.

To add the internal date to the calendar:

1.	Declare a private field to hold the date:

type
  TSampleCalendar = class(TCustomGrid)

 private
    FDate: TDateTime;

 ...

2.	Initialize the date field in the constructor:

constructor TSampleCalendar.Create(AOwner: TComponent);

begin
  inherited Create(AOwner); { this is already here }

 ... { other initializations here }

 FDate := Date; { get current date from RTL }
end;

3.	Declare a run-time property to allow access to the encoded date.

You'll need a method for setting the date, because setting the date requires updating the onscreen image of the control:

type
  TSampleCalendar = class(TCustomGrid)

 private
    procedure SetCalendarDate(Value: TDateTime);

 public
    property CalendarDate: TDateTime read FDate write SetCalendarDate;

 ...

procedure TSampleCalendar.SetCalendarDate(Value: TDateTime);
begin
  FDate := Value; { set new date value }

 Refresh; { update the onscreen image }

end;

Accessing the day, month, and year

An encoded numeric date is fine for applications, but humans prefer to work with days, months, and years. You can provide alternate access to those elements of the stored, encoded date by creating properties.

Because each element of the date (day, month, and year) is an integer, and because setting each requires encoding the date when set, you can avoid duplicating the code each time by sharing the implementation methods for all three properties. That is, you can write two methods, one to read an element and one to write one, and use those methods to get and set all three properties.

To provide design-time access to the day, month, and year:

1.	Declare the three properties, assigning each a unique index number:

type
  TSampleCalendar = class(TCustomGrid)

 public
    property Day: Integer index 3 read GetDateElement write SetDateElement;

   property Month: Integer index 2 read GetDateElement write SetDateElement;

   property Year: Integer index 1 read GetDateElement write SetDateElement;

 ...

2.	Declare and write the implementation methods, setting different elements for each index value:

type
  TSampleCalendar = class(TCustomGrid)

 private
    function GetDateElement(Index: Integer): Integer; { note the Index parameter }
    procedure SetDateElement(Index: Integer; Value: Integer);

 ...

function TSampleCalendar.GetDateElement(Index: Integer): Integer;

var
  AYear, AMonth, ADay: Word;

begin
  DecodeDate(FDate, AYear, AMonth, ADay); { break encoded date into elements }

 case Index of
    1: Result := AYear;

   2: Result := AMonth;

   3: Result := ADay;

   else Result := -1;

 end;
end;

procedure TSampleCalendar.SetDateElement(Index: Integer; Value: Integer);

var
  AYear, AMonth, ADay: Word;

begin
  if Value > 0 then	{ all elements must be positive }
  begin
    DecodeDate(FDate, AYear, AMonth, ADay);	{ get current date elements }

   case Index of	{ set new element depending on Index }
      1: AYear := Value;

     2: AMonth := Value;

     3: ADay := Value;

     else Exit;

   end;

   FDate := EncodeDate(AYear, AMonth, ADay); { encode the modified date }

   Refresh; { update the visible calendar }

 end;

end;

Now you can set the calendar's day, month, and year at design time using the Object Inspector or at runtime using code. Of course, you have not yet added the code to paint the dates into the cells, but now you have the needed data.


Generating the day numbers

Section topics 	See also

Putting numbers into the calendar involves several considerations. The number of days in the month depends on which month it is, and whether the given year is a leap year. In addition, months start on different days of the week, dependent on the month and year.

Add two more methods to the calendar class: a Boolean function that indicates whether the current year is a leap year, and an integer function that returns the number of days in the current month. Be sure to add the method declarations to the TSampleCalendar class declaration.

function TSampleCalendar.IsLeapYear: Boolean;
begin
  Result := (Year mod 4 = 0) 	{ years divisible by 4 are... }
    and ((Year mod 100 <> 0)	{ ...except century years... }
    or (Year mod 400 = 0));	{ ...unless it's divisible by 400 }
end;

function TSampleCalendar.DaysThisMonth: Integer;
const
  DaysPerMonth: array[1..12] of Integer =
    (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);	{ usual numbers of days }

begin
  if FDate = 0 then Result := 0	{ 0 indicates invalid date, so no days }

 else
  begin
    Result := DaysPerMonth[Month];	{ normally, just return number }

   if (Month = 2) and IsLeapYear then Inc(Result);	{ plus 1 in leap February }

 end;
end;

Note that the DaysThisMonth function returns zero if the stored date is empty. This allows applications to indicate an invalid date. By indicating that the month has no days, you produce a blank calendar to represent the invalid date.

Once you have the information on leap years and days per month, you can calculate where in the grid the individual dates go. The calculation is based on the day of the week the month starts on.

Because you'll need the month-offset number for each cell you fill in, the best practice is to calculate it once when you change the month or year, then refer to it each time. You can store the value in a class field, then update that field each time the date changes.

To fill in the days in the proper cells:

1.	Add a month-offset field to the object and a method that updates the field value:

type
  TSampleCalendar = class(TCustomGrid)
  private
    FMonthOffset: Integer; { storage for the offset }

 ...

 protected
    procedure UpdateCalendar; virtual; { property for offset access }
  end;

...

procedure TSampleCalendar.UpdateCalendar;

var
  AYear, AMonth, ADay: Word;
  FirstDate: TDateTime; { date of the first day of the month }

begin

 if FDate <> 0 then { only calculate offset if date is valid }
  begin
    DecodeDate(FDate, AYear, AMonth, ADay); { get elements of date }

   FirstDate := EncodeDate(AYear, AMonth, 1); { date of the first }

   FMonthOffset := 2 - DayOfWeek(FirstDate); { generate the offset into the grid }

 end;

 Refresh; { always repaint the control }

end;

2.	Add statements to the constructor and the SetCalendarDate and SetDateElement methods that call the new update method whenever the date changes:

constructor TSampleCalendar.Create(AOwner: TComponent);

begin
  inherited Create(AOwner); { this is already here }

 ... { other initializations here }

 UpdateCalendar; { set proper offset }
end;

procedure TSampleCalendar.SetCalendarDate(Value: TDateTime);

begin
  FDate := Value; { this was already here }

 UpdateCalendar; { this previously called Refresh }
end;

procedure TSampleCalendar.SetDateElement(Index: Integer; Value: Integer);
begin
  ...

   FDate := EncodeDate(AYear, AMonth, ADay); { encode the modified date }

   UpdateCalendar; { this previously called Refresh }

 end;

end;

3.	Add a method to the calendar that returns the day number when passed the row and column coordinates of a cell:

function TSampleCalendar.DayNum(ACol, ARow: Integer): Integer;

begin

 Result := FMonthOffset + ACol + (ARow - 1) * 7; { calculate day for this cell }

 if (Result < 1) or (Result > DaysThisMonth) then Result := -1; { return -1 if invalid }

end;

Remember to add the declaration of DayNum to the component's type declaration.

4.	Now that you can calculate where the dates go, you can update DrawCell to fill in the dates:

procedure TCalendar.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);

var
  TheText: string;
  TempDay: Integer;

begin
  if ARow = 0 then { if this is the header row ...}

   TheText := ShortDayNames[ACol + 1] { just use the day name }

 else

 begin
    TheText := ''; { blank cell is the default }

   TempDay := DayNum(ACol, ARow); { get number for this cell }

   if TempDay <> -1 then TheText := IntToStr(TempDay); { use the number if valid }

 end;

 with ARect, Canvas do

   TextRect(ARect, Left + (Right - Left - TextWidth(TheText)) div 2,
      Top + (Bottom - Top - TextHeight(TheText)) div 2, TheText);

end;

Now if you reinstall the calendar component and place one on a form, you'll see the proper information for the current month.


Selecting the current day

Section topics 	See also

Now that you have numbers in the calendar cells, it makes sense to move the selection highlighting to the cell containing the current day. By default, the selection starts on the top left cell, so you need to set the Row and Column properties both when constructing the calendar initially and when the date changes.

To set the selection on the current day, change the UpdateCalendar method to set Row and Column before calling Refresh:

procedure TSampleCalendar.UpdateCalendar;

begin

 if FDate <> 0 then

 begin
    ... { existing statements to set FMonthOffset }

   Row := (ADay - FMonthOffset) div 7 + 1;

   Col := (ADay - FMonthOffset) mod 7;

 end;

 Refresh; { this is already here }

end;

Note that you're now reusing the ADay variable previously set by decoding the date.


Navigating months and years

Section topics 	See also

Properties are useful for manipulating components, especially at design time. But sometimes there are types of manipulations that are so common or natural, often involving more than one property, that it makes sense to provide methods to handle them. One example of such a natural manipulation is a "next month" feature for a calendar. Handling the wrapping around of months and incrementing of years is simple, but very convenient for the developer using the component.

The only drawback to encapsulating common manipulations into methods is that methods are available only at runtime. However, such manipulations are generally only cumbersome when performed repeatedly, and that's fairly rare at design time.

For the calendar, add the following four methods for next and previous month and year:

procedure TCalendar.NextMonth;
begin
  if Month < 12 then { if it's not December... }
    Month := succ(Month) { ...just increment the month }

 else
  begin
    Year := Year + 1; { otherwise, it's next year... }

   Month := 1; { ...and January }

 end;
end;

procedure TCalendar.PrevMonth;
begin
  if Month > 1 then { if not January... }
    Month := pred(Month) { ...just decrement month }

 else
  begin
    Year := Year - 1; { otherwise, it's the previous year... }

   Month := 12; { ...and December }

 end;
end;

procedure TCalendar.NextYear;
begin
  Year := Year + 1;{ increment year }
end;

procedure TCalendar.PrevYear;
begin
  Year := Year - 1; { decrement year }
end;

Be sure to add the declarations of the new methods to the class declaration.

Now when you create an application that uses the calendar component, you can easily implement browsing through months or years.


Navigating days

Section topics 	See also

Within a given month, there are two obvious ways to navigate among the days. The first is to use the arrow keys, and the other is to respond to clicks of the mouse. The standard grid component handles both as if they were clicks. That is, an arrow movement is treated like a click on an adjacent cell.


Moving the selection

Section topics 	See also

The inherited behavior of a grid handles moving the selection in response to either arrow keys or clicks, but if you want to change the selected day, you need to modify that default behavior.

To handle movements within the calendar, override the Click method of the grid.

When you override a method such as Click that is tied in with user interactions, you will nearly always include a call to the inherited method, so as not to lose the standard behavior.

The following is an overridden Click method for the calendar grid. Be sure to add the declaration of Click to TSampleCalendar, including the override directive afterward.

procedure TSampleCalendar.Click;
var
  TempDay: Integer;

begin
  inherited Click; { remember to call the inherited method! }

 TempDay := DayNum(Col, Row); { get the day number for the clicked cell }

 if TempDay <> -1 then Day := TempDay; { change day if valid }
end;


Providing an OnChange event

Section topics 	See also

Now that users of the calendar can change the date within the calendar, it makes sense to allow applications to respond to those changes.

To add an OnChange event to TSampleCalendar:

1.	Declare the event, a field to store the event, and a dynamic method to call the event:

type
  TSampleCalendar = class(TCustomGrid)

 private
    FOnChange: TNotifyEvent;

 protected
    procedure Change; dynamic;

 ...

 published
    property OnChange: TNotifyEvent read FOnChange write FOnChange;

 ...

2.	Write the Change method:

procedure TSampleCalendar.Change;

begin

 if Assigned(FOnChange) then FOnChange(Self);

end;

3.	Add statements calling Change to the end of the SetCalendarDate and SetDateElement methods:

procedure TSampleCalendar.SetCalendarDate(Value: TDateTime);

begin

 FDate := Value;

 UpdateCalendar;

 Change; { this is the only new statement }

end;

procedure TSampleCalendar.SetDateElement(Index: Integer; Value: Integer);

begin

   ... { many statements setting element values }

   FDate := EncodeDate(AYear, AMonth, ADay);

   UpdateCalendar;

   Change; { this is new }

 end;

end;

Applications using the calendar component can now respond to changes in the date of the component by attaching handlers to the OnChange event.


Excluding blank cells

Section topics 	See also

As the calendar is written, the user can select a blank cell, but the date does not change. It makes sense, then, to disallow selection of the blank cells.

To control whether a given cell is selectable, override the SelectCell method of the grid.

SelectCell is a function that takes a column and row as parameters, and returns a Boolean value indicating whether the specified cell is selectable.

You can override SelectCell to return false if the cell does not contain a valid date:

function TSampleCalendar.SelectCell(ACol, ARow: Longint): Boolean;

begin

 if DayNum(ACol, ARow) = -1 then Result := False { -1 indicates invalid date }
  else Result := inherited SelectCell(ACol, ARow); { otherwise, use inherited value }

end;

Now if the user clicks a blank cell or tries to move to one with an arrow key, the calendar leaves the current cell selected.


See also

TCustomGrid

Creating new message handlers


Making a control data-aware

Section topics 	See also

When working with database connections, it is often convenient to have controls that are data-aware. That is, the application can establish a link between the control and some part of a database. Delphi includes data-aware labels, edit boxes, list boxes, combo boxes, lookup controls, and grids. You can also make your own controls data-aware.

There are several degrees of data awareness. The simplest is read-only data awareness, or data browsing, the ability to reflect the current state of a database. More complicated is editable data awareness, or data editing, where the user can edit the values in the database by manipulating the control. Note also that the degree of involvement with the database can vary, from the simplest case, a link with a single field, to more complex cases, such as multiple-record controls.

These topics first illustrate the simplest case, making a read-only control that links to a single field in a dataset. The specific control used will be the calendar created in "Customizing a grid," TSampleCalendar. You can also use the standard calendar control on the Samples page of the Component palette, TCalendar.

The topics then explain how to make the new data-browsing control a data-editing control.


Creating a data-browsing control

Section topics 	See also

Creating a data-aware calendar control, whether it is a read-only control or one in which the user can change the underlying data in the dataset, involves the following steps:

Creating and registering the component

Adding the data link

Responding to data changes


Creating and registering the component

Section topics 	See also

Creation of every component begins the same way: You create a unit, derive a component class, register it, compile it, and install it on the Component palette.

For this example, follow the general procedure for creating a component, with these specifics:

Call the component's unit DBCal.

Derive a new component class called TDBCalendar, descended from TSampleCalendar.Register TDBCalendar on the Samples page of the Component palette.

Example

The resulting unit should look like this:

unit DBCal;

interface

uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Grids, Calendar;

type
  TDBCalendar = class(TSampleCalendar)
  end;

procedure Register;

implementation

procedure Register;
begin

 RegisterComponents('Samples', [TDBCalendar]);
end;

end.

You can now proceed with making the new calendar a data browser.


Making the control read-only

Section topics 	See also

Because this data calendar will be read-only with respect to the data, it makes sense to make the control itself read-only, so users won't make changes within the control and expect them to be reflected in the database.

Making the calendar read-only involves:

Adding the ReadOnly property

Allowing needed updates

Note that if you started with the TCalendar component from Delphi's Samples page instead of TSampleCalendar, it already has a ReadOnly property, so you can skip these steps.

Adding the ReadOnly property

By adding a ReadOnly property, you'll provide a way to make the control read-only at design time. When that property is set to True, you can make all cells in the control unselectable.

To add the ReadOnly property:

1.	Add the property declaration and a private field to hold the value:

type
  TDBCalendar = class(TSampleCalendar)

 private
    FReadOnly: Boolean; { field for internal storage }

 public
    constructor Create(AOwner: TComponent); override; { must override to set default }

 published
    property ReadOnly: Boolean read FReadOnly write FReadOnly default True;

 end;

...

constructor TDBCalendar.Create(AOwner: TComponent);

begin

 inherited Create(AOwner); { always call the inherited constructor! }

 FReadOnly := True; { set the default value }

end;

2.	Override the SelectCell method to disallow selection if the control is read-only. Use of SelectCell is explained in "Excluding blank cells."

function TDBCalendar.SelectCell(ACol, ARow: Longint): Boolean;

begin

 if FReadOnly then Result := False { cannot select if read only }

 else Result := inherited SelectCell(ACol, ARow); { otherwise, use inherited method }

end;

Remember to add the declaration of SelectCell to the type declaration of TDBCalendar, and append the override directive.

If you now add the calendar to a form, you'll find that the component ignores clicks and keystrokes. It also fails to update the selection position when you change the date.

Allowing needed updates

The read-only calendar uses the SelectCell method for all kinds of changes, including setting the Row and Col properties. The UpdateCalendar method sets Row and Col every time the date changes, but because SelectCell disallows changes, the selection remains in place, even though the date changes.

To get around this absolute prohibition on changes, you can add an internal Boolean flag to the calendar, and permit changes when that flag is set to True:

type
  TDBCalendar = class(TSampleCalendar)

 private
    FUpdating: Boolean; { private flag for internal use }

 protected
    function SelectCell(ACol, ARow: Longint): Boolean; override;

 public
    procedure UpdateCalendar; override; { remember the override directive }

 end;

...

function TDBCalendar.SelectCell(ACol, ARow: Longint): Boolean;

begin

 if (not FUpdating) and FReadOnly then Result := False { allow select if updating }

 else Result := inherited SelectCell(ACol, ARow); { otherwise, use inherited method }
end;

procedure TDBCalendar.UpdateCalendar;

begin
  FUpdating := True; { set flag to allow updates }

 try
    inherited UpdateCalendar; { update as usual }

 finally
    FUpdating := False; { always clear the flag }
  end;
end;

The calendar still disallows user changes, but now correctly reflects changes made in the date by changing the date properties. Now that you have a true read-only calendar control, you are ready to add the data-browsing ability.


Adding the data link

Section topics 	See also

The connection between a control and a database is handled by a class called a data link. The data-link class that connects a control with a single field in a database is TFieldDataLink. There are also data links for entire tables.

A data-aware control owns its data-link class. That is, the control has the responsibility for constructing and destroying the data link.

To establish a data link as an owned class:

1.	Declaring the class field

2.	Declaring the access properties

3.	Initializing the data link

Declaring the class field

A component needs a field for each of its owned classes, as explained in "Declaring the class fields." In this case, the calendar needs a field of type TFieldDataLink for its data link.

Declare a field for the data link in the calendar:

type
  TDBCalendar = class(TSampleCalendar)

 private
    FDataLink: TFieldDataLink;

 ...

 end;

Before you can compile the application, you need to add DB and DBCtrls to the unit's uses clause.

Declaring the access properties

Every data-aware control has a DataSource property that specifies which data-source class in the application provides the data to the control. In addition, a control that accesses a single field needs a DataField property to specify that field in the data source.

These access properties do not provide access to the owned classes themselves, but rather to the corresponding properties in the owned class. That is, you'll create properties that enable the control and its data link to share the same data source and field.

Declare the DataSource and DataField properties and their implementation methods, then write the methods as "pass-through" methods to the corresponding properties of the data-link class

Example

type
  TDBCalendar = class(TSampleCalendar)

 private { implementation methods are private }
    ...
    function GetDataField: string; { returns the name of the data field }

   function GetDataSource: TDataSource; { returns reference to the data source }

   procedure SetDataField(const Value: string); { assigns name of data field }

   procedure SetDataSource(Value: TDataSource); { assigns new data source }

 published	{ make properties available at design time }
    property DataField: string read GetDataField write SetDataField;

   property DataSource: TDataSource read GetDataSource write SetDataSource;

 end;

...

function TDBCalendar.GetDataField: string;
begin
  Result := FDataLink.FieldName;
end;

function TDBCalendar.GetDataSource: TDataSource;
begin
  Result := FDataLink.DataSource;
end;

procedure TDBCalendar.SetDataField(const Value: string);
begin
  FDataLink.FieldName := Value;
end;

procedure TDBCalendar.SetDataSource(Value: TDataSource);
begin
  FDataLink.DataSource := Value;
end;

Initializing the data link

A data-aware control needs access to its data link throughout its existence, so it must construct the data-link object as part of its own constructor, and destroy the data-link object before it is itself destroyed.

Override the Create and Destroy methods of the calendar to construct and destroy the data-link object, respectively:

type
  TDBCalendar = class(TSampleCalendar)

 public { constructors and destructors are always public }
    constructor Create(AOwner: TComponent); override;

   destructor Destroy; override;
    ...

 end;

...

constructor TDBCalendar.Create(AOwner: TComponent);

begin
  FDataLink := TFieldDataLink.Create; { construct the data-link object }
  inherited Create(AOwner); { always call the inherited constructor first }

 FReadOnly := True; { this is already here }

end;

destructor TDBCalendar.Destroy;

begin
  FDataLink.Free; { always destroy owned objects first... }

 inherited Destroy; { ...then call inherited destructor }

end;


Responding to data changes

Section topics 	See also

Once a control has a data link and properties to specify the data source and data field, it needs to respond to changes in the data in that field, either because of a move to a different record or because of a change made to that field.

Data-link classes all have events named OnDataChange. When the data source indicates a change in its data, the data-link object calls any event handler attached to its OnDataChange event.

To update a control in response to data changes, attach a handler to the data link's OnDataChange event.

In this case, you'll add a method to the calendar, then designate it as the handler for the data link's OnDataChange.

Declare DataChange method, then assign it to the data link's OnDataChange event.

Example

type
  TDBCalendar = class(TSampleCalendar)

 private { this is an internal detail, so make it private }
    procedure DataChange(Sender: TObject); { must have proper parameters for event }

 end;

...

constructor TDBCalendar.Create(AOwner: TComponent);

begin
  inherited Create(AOwner); { always call the inherited constructor first }

 FReadOnly := True; { this is already here }

 FDataLink := TFieldDataLink.Create; { construct the data-link object }

 FDataLink.OnDataChange := DataChange; { attach handler to event }

end;

destructor TDBCalendar.Destroy;

begin
  FDataLink.OnDataChange := nil; { detach handler before destroying object }
  FDataLink.Free; { always destroy owned objects first... }

 inherited Destroy; { ...then call inherited destructor }

end;

procedure TDBCalendar.DataChange(Sender: TObject);

begin

 if FDataLink.Field = nil then { if there is no field assigned... }
    CalendarDate := 0 { ...set to invalid date }

 else CalendarDate := FDataLink.Field.AsDateTime; { otherwise, set calendar to the date }

end;


Creating a data-editing control

Section topics 	See also

When you create a data-editing control, you create and register the component and add the data link just as you do for a data-browsing control. You also respond to data changes in the underlying field in a similar manner, but you must handle a few more issues.

For example, you probably want your control to respond to both key and mouse events. Your control must respond when the user changes the contents of the control. When the user exits the control, you want the changes made in the control to be reflected in the dataset.

The data-editing control described here is the same calendar control described in the first part of these topics. The control is modified so that it can edit as well as view the data in its linked field.

To modify the existing control and make it a data-editing control:

1.	Changing the default value of FReadOnly

2.	Handling mouse-down and key-down messages

3.	Updating the field data-link class

4.	Modifying the Change method

5.	Updating the dataset


Changing the default value of FReadOnly

Section topics 	See also

Because this is a data-editing control, the ReadOnly property should be set to False by default. To make the ReadOnly property False, change the value of FReadOnly in the constructor:

constructor TDBCalendar.Create(AOwner: TComponent);

begin

 ...

 FReadOnly := False;	{ set the default value }

 ...

end;


Handling mouse-down and key-down messages

Section topics 	See also

When the user of the control begins interacting with it, the control receives either mouse-down messages (WM_LBUTTONDOWN, WM_MBUTTONDOWN, or WM_RBUTTONDOWN) or a key-down message (WM_KEYDOWN) from Windows. To enable a control to respond to these messages, you must write handlers that respond to these messages.

Responding to mouse-down messages

A MouseDown method is a protected method for a control's OnMouseDown event. The control itself calls MouseDown in response to a Windows mouse-down message. When you override the inherited MouseDown method, you can include code that provides other responses in addition to calling the OnMouseDown event.

To override MouseDown, add the MouseDown method to the TDBCalendar class:

type
  TDBCalendar = class(TSampleCalendar);

  ...

 protected
    procedure MouseDown(Button: TButton, Shift: TShiftState, X: Integer, Y: Integer);       override;

  ...

 end;

procedure TDBCalendar.MouseDown(Button: TButton; Shift: TShiftState; X, Y: Integer);

var

 MyMouseDown: TMouseEvent;

begin

 if not ReadOnly and FDataLink.Edit then

   inherited MouseDown(Button, Shift, X, Y)

 else

 begin

   MyMouseDown := OnMouseDown;

   if Assigned(MyMouseDown then MyMouseDown(Self, Button, Shift, X, Y);

 end;

end;

When MouseDown responds to a mouse-down message, the inherited MouseDown method is called only if the control's ReadOnly property is False and the data-link object is in edit mode, which means the field can be edited. If the field cannot be edited, the code the programmer put in the OnMouseDown event handler, if one exists, is executed.

Responding to key-down messages

A KeyDown method is a protected method for a control's OnKeyDown event. The control itself calls KeyDown in response to a Windows key-down message. When overriding the inherited KeyDown method, you can include code that provides other responses in addition to calling the OnKeyDown event.

To override KeyDown:

1.	Add a KeyDown method to the TDBCalendar class:

type
  TDBCalendar = class(TSampleCalendar);

  ...

 protected
    procedure KeyDown(var Key: Word; Shift: TShiftState; X: Integer; Y: Integer);       override;

  ...

 end;

2.	Implement the KeyDown method:

procedure KeyDown(var Key: Word; Shift: TShiftState);

var

 MyKeyDown: TKeyEvent;

begin

 if not ReadOnly and (Key in [VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_END,

   VK_HOME, VK_PRIOR, VK_NEXT]) and FDataLink.Edit then

   inherited KeyDown(Key, Shift)

 else

 begin

   MyKeyDown := OnKeyDown;

   if Assigned(MyKeyDown) then MyKeyDown(Self, Key, Shift);

 end;

end;

When KeyDown responds to a mouse-down message, the inherited KeyDown method is called only if the control's ReadOnly property is False, the key pressed is one of the cursor control keys, and the data-link object is in edit mode, which means the field can be edited. If the field cannot be edited or some other key is pressed, the code the programmer put in the OnKeyDown event handler, if one exists, is executed.


Updating the field data-link class

Section topics 	See also

There are two types of data changes:

A change in a field value that must be reflected in the data-aware control.

A change in the data-aware control that must be reflected in the field value.

The TDBCalendar component already has a DataChange method that handles a change in the field's value in the dataset by assigning that value to the CalendarDate property. The DataChange method is the handler for the OnDataChange event. So the calendar component can handle the first type of data change.

Similarly, the field data-link class also has an OnUpdateData event that occurs as the user of the control modifies the contents of the data-aware control. The calendar control has a UpdateData method that becomes the event handler for the OnUpdateData event. UpdateData assigns the changed value the data-aware control to the field data link.

To update the field data-link class:

1.	To reflect a change made to the value in the calendar in the field value, add an UpdateData method to the private section of the calendar component:

type
  TDBCalendar = class(TSampleCalendar);

 private
    procedure UpdateData(Sender: TObject);

  ...

 end;

2.	Implement the UpdateData method:

procedure UpdateData(Sender: TObject);

begin

 FDataLink.Field.AsDateTime := CalendarDate; { set field link to calendar date }

end;


Modifying the Change method

Section topics 	See also

The Change method of the TDBCalendar is called whenever a new date value is set. Change calls the OnChange event handler, if one exists. The component user can write code in the OnChange event handler to respond to changes in the date.

When the calendar date changes, the underlying dataset should be notified that a change has occurred. You can do that by overriding the Change method and adding one more line of code.

To modify the Change method:

1.	Add a new Change method to the TDBCalendar component:

type
  TDBCalendar = class(TSampleCalendar);

 private
    procedure Change; override;

  ...

 end;

2.	Write the Change method, calling the Modified method that informs the dataset the data has changed, then call the inherited Change method:

TDBCalendar.Change;

begin

 FDataLink.Modified; { call the Modified method }

 inherited Change; { call the inherited Change method }

end;


Updating the dataset

Section topics 	See also

So far, a change within the data-aware control has changed values in the field data-link class. The final step in creating a data-editing control is to update the dataset with the new value. This should happen after the person changing the value in the data-aware control exits the control by clicking outside the control or pressing the Tab key.

VCL has defined message control IDs for operations on controls. For example, the CM_EXIT message is sent to the control when the user exits the control. You can write message handlers that respond to the message. In this case, when the user exits the control, the CMExit method, the message handler for CM_EXIT, responds by updating the record in the dataset with the changed values in the field data-link class.For more information about message handlers, see "Handling messages."

To update the dataset within a message handler:

1.	Add the message handler to the TDBCalendar component:

type
  TDBCalendar = class(TSampleCalendar);

 private
    procedure CMExit(var Message: TWMNoParams); message CM_EXIT;

  ...

 end;

2.	Implement the CMExit method so it looks something like this:

procedure TDBCalendar.CMExit(var Message: TWMNoParams);

begin

 try

   FDataLink.UpdateRecord; { tell data link to update database }

 except

   on Exception do SetFocus; { if it failed, don't let focus leave }

 end;

 inherited;

end;


See also

Customizing a grid

Understanding datasets

Handling messages

About user events


Making a dialog box a component

Section topics 	See also

You will find it convenient to make a frequently used dialog box a component that you can add to the Component palette. Your dialog-box components will work just like the components that represent the standard Windows common dialog boxes. The goal is to create a simple component that a user can add to a project and set properties for at design time.

Making a dialog box a component requires these steps:

1.	Defining the component interface

2.	Creating and registering the component

3.	Creating the component interface

4.	Testing the component

The Delphi "wrapper" component associated with the dialog box creates and executes the dialog box at runtime, passing along the data the user specified. The dialog-box component is therefore both reusable and customizable.

In these topics, you'll see how to create a wrapper component around the generic About Box form provided in the Delphi Object Repository.

Note:	Copy the files ABOUT.PAS and ABOUT.DFM into your working directory.

There aren't many special considerations for designing a dialog box that will be wrapped into a component. Nearly any form can operate as a dialog box in this context.


Defining the component interface

Section topics 	See also

Before you can create the component for your dialog box, you need to decide how you want developers to use it. You create an interface between your dialog box and applications that use it.

For example, look at the properties for the common dialog box components. They enable the developer to set the initial state of the dialog box, such as the caption and initial control settings, then read back any needed information after the dialog box closes. There's no direct interaction with the individual controls in the dialog box, just with the properties in the wrapper component.

The interface must therefore contain enough information that the dialog-box form can appear in the way the developer specifies and return any information the application needs. You can think of the properties in the wrapper component as being persistent data for a transient dialog box.


Creating and registering the component

Section topics 	See also

Creation of every component begins the same way: You create a unit, derive a component class, register it, compile it, and install it on the Component palette.

For this example, follow the general procedure for creating a component, with these specifics:

Call the component's unit AboutDlg.

Derive a new component type called TAboutBoxDlg, descended from TComponent.

Register TAboutBoxDlg on the Samples page of the Component palette.

The resulting unit should look like this:

unit AboutDlg;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms;

type
  TAboutBoxDlg = class(TComponent)
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Samples', [TAboutBoxDlg]);
end;

end.

The new component now has only the capabilities built into TComponent. It is the simplest nonvisual component. In the next section, you'll create the interface between the component and the dialog box.


Creating the component interface

Section topics 	See also

To create the component interface:

1.	Including the form unit files

2.	Adding interface properties

3.	Adding the Execute method


Including the form unit

Section topics 	See also

For your wrapper component to initialize and display the wrapped dialog box, you must add the form's unit to the uses clause of the wrapper component's unit.

Append About to the uses clause of the AboutDlg unit.

The uses clause now looks like this:

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms
  About;

The form unit always declares an instance of the form class. In the case of the About box, the form class is TAboutBox, and the About unit includes the following declaration:

var
  AboutBox: TAboutBox;

So by adding About to the uses clause, you make AboutBox available to the wrapper component.


Adding interface properties

Section topics 	See also

Before proceeding, decide on the properties your wrapper needs to enable developers to use your dialog box as a component in their applications. Then, you can add declarations for those properties to the component's class declaration.

Properties in wrapper components are somewhat simpler than the properties you would create if you were writing a regular component. Remember that in this case, you're just creating some persistent data that the wrapper can pass back and forth to the dialog box. By putting that data in the form of properties, you enable developers to set data at design time so that the wrapper can pass it to the dialog box at runtime.

Declaring an interface property requires two additions to the component's class declaration:

A private class field, which is a variable the wrapper uses to store the value of the property

The published property declaration itself, which specifies the name of the property and tells it which field to use for storage

Interface properties of this sort do not need access methods. They use direct access to their stored data. By convention, the class field that stores the property's value has the same name as the property, but with the letter F in front. The field and the property must be of the same type.

Example

For example, to declare an integer-type interface property called Year:

type

 TMyWrapper = class(TComponent)

 private
    FYear: Integer; { field to hold the Year-property data }

 published
    property Year: Integer read FYear write FYear; { property matched with storage }

 end;

For this About box, you need four string-type properties--one each for the product name, the version information, the copyright information, and any comments.

type

 TAboutBoxDlg = class(TComponent)

 private
    FProductName, FVersion, FCopyright, FComments: string; { declare fields }

 published
    property ProductName: string read FProductName write FProductName;

   property Version: string read FVersion write FVersion;

   property Copyright: string read FCopyright write FCopyright;

   property Comments: string read FComments write FComments;

 end;

When you install the component onto the Component palette and place the component on a form, you'll be able to set the properties, and those values will automatically stay with the form. The wrapper can then use those values when executing the wrapped dialog box.


Adding the Execute method

Section topics 	See also

The final part of the component interface is a way to open the dialog box and return a result when it closes. As with the common-dialog-box components, you'll use a Boolean function called Execute that returns True if the user clicks OK, or False if the user cancels the dialog box.

The declaration for the Execute method always looks like this:

type
  TMyWrapper = class(TComponent)

 public
    function Execute: Boolean;
  end;

The minimum implementation for Execute needs to construct the dialog box form, show it as a modal dialog box, and return either True or False, depending on the return value from ShowModal.

Example

Here's the minimal Execute method, for a dialog-box form of type TMyDialogBox:

function TMyWrapper.Execute: Boolean;

begin

 DialogBox := TMyDialogBox.Create(Application); { construct the form }

 try
    Result := (DialogBox.ShowModal = IDOK); 	{ execute; set result based on how closed }

 finally
    DialogBox.Free; { dispose of the form }

 end;

end;

Note the use of a try..finally block to ensure that the application disposes of the dialog-box object even if an exception occurs. In general, whenever you construct an object this way, you should use a try..finally block to protect the block of code and make certain the application frees any resources it allocates.

In practice, there will be more code inside the try..finally block. Specifically, before calling ShowModal, the wrapper will set some of the dialog box's properties based on the wrapper component's interface properties. After ShowModal returns, the wrapper will probably set some of its interface properties based on the outcome of the dialog box execution.

In the case of the About box, you need to use the wrapper component's four interface properties to set the contents of the labels in the About box form. Because the About box doesn't return any information to the application, there's no need to do anything after calling ShowModal. Write the About-box wrapper's Execute method so that it looks like this:

Within the public part of the TAboutDlg class, add the declaration for the Execute method:

type

 TAboutDlg = class(TComponent)

public

 function Execute: Boolean;

end;

function TAboutBoxDlg.Execute: Boolean;
begin
  AboutBox := TAboutBox.Create(Application); { construct About box }

 try
    if ProductName = '' then { if product name's left blank... }

     ProductName := Application.Title; { ...use application title instead }

   AboutBox.ProductName.Caption := ProductName; { copy product name }

   AboutBox.Version.Caption := Version; { copy version info }

   AboutBox.Copyright.Caption := Copyright; { copy copyright info }

   AboutBox.Comments.Caption := Comments; { copy comments }

   AboutBox.Caption := 'About ' + ProductName; { set About-box caption }

   with AboutBox do
    begin

     ProgramIcon.Picture.Graphic := Application.Icon; { copy icon }

     Result := (ShowModal = IDOK); { execute and set result }

   end;

 finally
    AboutBox.Free; { dispose of About box }

 end;

end;


Testing the component

Section topics 	See also

Once you've installed the dialog-box component, you can use it as you would any of the common dialog boxes, by placing one on a form and executing it. A quick way to test the About box is to add a command button to a form and execute the dialog box when the user clicks the button.

For example, if you created an About dialog box, made it a component, and added it to the Component palette, you could test it with the following steps:

1.	Create a new project.

2.	Place an About-box component on the main form.

3.	Place a command button on the form.

4.	Double-click the command button to create an empty click-event handler.

5.	In the click-event handler, type the following line of code:

  AboutBoxDlg1.Execute;

6.	Run the application.

When the main form appears, click the command button. The About box appears with the default project icon and the name Project1. Choose OK to close the dialog box.

You can further test the component by setting the various properties of the About-box component and again running the application.


See also

Common component tasks

Component palette

Customizing the Component Palette


Building a dialog box into a DLL

Section topics 	See also

You may want to use the same dialog box in both Delphi applications and programs created with other tools. In this case, you can build the component into a standard dynamic-link library (DLL) that can be called from any Windows program.

Note:	Only components derived from TForm can be turned into standalone DLLs. You can also create DLLs that encapsulate . Individual components such as buttons cannot be compiled (outside of forms) for use in non-Delphi applications.

Unlike Delphi packages, which utilize external components indirectly through the requires clause, each DLL must contain the overhead of the entire component library (about 100K). You can minimize this overhead by putting several dialog boxes into a single DLL.

For example, suppose you have a suite of applications that all use the same dialog boxes for checking passwords, displaying shared data, or updating status information. You can put all of these dialog boxes into a single DLL, allowing them to share the component-library overhead.

Note:	Before working with DLLs, make sure that .DLL file display is enabled in Windows. Open the Windows Explorer, choose View|Options, and click the View tab. If the .DLL file type is marked as hidden, the files will not appear in file-selection dialogs.

Building a dialog box into a DLL involves three procedures:

Adding the interface routine

Modifying the project file

Opening the dialog box from an application


Adding the interface routine

Section topics 	See also

When wrapping a dialog box into a DLL, you need to provide an interface routine for the dialog box. This is similar to creating a wrapper component for a dialog box. However, the application calling for the dialog box might not be written with Delphi, so you need to export plain procedure or function interfaces instead of components with methods and properties.

The simplest and most useful way to create the interface routine is to create a single function that takes parameters representing any properties the developer might want to set in the dialog box, returning a value describing the outcome of the dialog-box session. If the dialog box isn't going to return any information to the application, you could use a procedure instead of a function.

You add the declaration of the interface routine to the interface section of the dialog box's form unit and implement the routine in the implementation section.


Declaring the interface routine

Section topics 	See also

Adding an interface-routine declaration involves declaring a single procedure or function in the interface section of the form unit, following the declaration with the export directive to assure the routine is accessible to outside code.

When writing interface routines that will be called from languages other than Object Pascal, be sure to declare parameters and return values using types that are available in the calling language. For example, you should pass strings as null-terminated arrays of characters (the Object Pascal type PChar) rather than Object Pascal's native string type.

Example

In a form unit containing a dialog-box form of type TPasswordForm, you would add the following declaration to the interface section:

unit PassForm;

interface

uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Forms, Controls,
  Forms, Dialogs, StdCtrls, Buttons;

type
  TPasswordForm = class(TForm)
  ...	{ various declarations go here }
  end;

var
  PasswordForm: TPasswordForm;

function GetPassword(APassword: PChar): WordBool; export;

The routine must have the export directive following it. That ensures that the DLL can export the routine to outside programs.


Implementing the interface routine

Section topics 	See also

Once you've declared the interface routine, all that remains is to implement it. The basic outline of an interface routine looks like this:

function DoDialog(...): ReturnType; { could be a procedure instead }
begin
  Result := DefaultValue; { set a default function result }

 DialogBox := TDialogBoxType.Create(Application); { construct the instance }

 try
    if DialogBox.ShowModal = IDOK then { execute and test result }

     { user accepted dialog box, set Result appropriately }

   else
      { user canceled or closed dialog box, react accordingly };

 finally
    DialogBox.Free; { dispose of the dialog box instance }

 end;
end;

Example

Here's an example that executes a password-entry dialog box and returns true if the password the user types matches the one passed as a parameter:

function GetPassword(APassword: PChar): WordBool;
begin
  Result := False;	                                 { start by assuming no correct entry }

 PasswordForm := TPasswordForm.Create(Application);

 try
    if PasswordForm.ShowModal = IDOK then               	{ test password if OK clicked }

     if CompareText(PasswordForm.PasswordField.Text, StrPas(APassword)) = 0 then
        Result := true
      else MessageDlg('Invalid password', mtWarning, [mbOK], 0);

 finally
    PasswordForm.Free;
  end;
end;

Before comparing the strings, you need to convert the null-terminated string into an Object Pascal string by calling the StrPas function.


Modifying the project file

Section topics 	See also

Once you've created an interface routine for a dialog box, you're ready to modify the project file to create a DLL instead of an application. There are four simple changes you need to make in the project file:

Change the reserved word program in the first line of the file to library.

Remove the Forms unit from the project's uses clause.

Remove all lines of code between the begin and end at the bottom of the file.

Below the uses clause, and before the begin..end block, add the reserved word exports, followed by the name of the dialog box interface routine and a semicolon.

Now, when you compile the project, it produces a DLL instead of an application. Applications can now call the DLL to open the wrapped dialog box.

Example

Here is an example of a typical project file before modification:

program Password;

uses
  Forms,
  PassForm in 'PASSFORM.PAS' {PasswordForm};

{$R *.RES}

begin
  Application.CreateForm(TPasswordForm, PasswordForm);
  Application.Run;
end.

Here is the same file, modified to create a DLL with an interface function called GetPassword:

library Password; { reserved word changed }

uses	 { removed Forms, }
  PassForm in 'PASSFORM.PAS' {PasswordForm};

exports
  GetPassword; { add exports clause }

{$R *.RES}

begin	 { remove code from main block }
end.


Opening the dialog box from an application

Section topics 	See also

Once you finish wrapping your dialog box into a DLL and compile the DLL, you're ready to use the dialog box from an application.

To use a dialog box from a DLL:

1.	Import the interface routine from the DLL.

2.	Call the interface routine.

These steps are the same when calling the DLL from any application, no matter what tools the application was created with. The following sections demonstrate briefly how to call the same function in the same DLL from different environments.


Opening the dialog box from a Delphi application

Section topics 	See also

Importing an interface routine is simple: inside the unit where you need to call the routine, you repeat its declaration, appending the reserved word external and the name of the DLL.

For example, to import an interface routine called GetPassword from a DLL called PASSWORD.DLL, you add the following to the importing unit:

function GetPassword(APassword: PChar): WordBool; far;
external 'PASSWORD';

Code in the unit that imports the routine can then call the routine normally.

For example, to test the import of GetPassword, you could create a simple project with a single command button on the main form. After importing the interface routine for the password-entry dialog box, you could add the following click-event handler that uses the password-entry dialog box:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if GetPassword('PASSWORD') then
    Color := clGreen

 else
    Color := clRed;
end;

If the user enters the correct password, the application turns the form green. If the user enters an incorrect password or cancels the dialog box, the form turns red.


Opening the dialog box from a Paradox application

Section topics 	See also

Importing an interface routine into a Paradox for Windows application is similar to importing the routine into a Delphi application. Because Paradox does not have units, you modify the Uses window of the object that will call the routine.

For example, if a button will call the GetPassword function in a DLL called PASSWORD.DLL, you modify the button's Uses window to include the following:

Uses Password
  GetPassword(APassword CPTR) CWORD
endUses

You can then test the import much as you would in Delphi. Create a new form that contains a button object and import GetPassword from PASSWORD.DLL. You can then modify the pushButton method of the button to call GetPassword:

method pushButton(var eventInfo Event)

  if GetPassword("PASSWORD") <> 0 then

     #Page2.Color = Green

  else

     #Page2.Color = Red

  endIf

endmethod


Opening the dialog box from a dBASE application

Section topics 	See also

Importing an interface routine into a dBASE for Windows application is similar to importing the routine into a Delphi application.

Here is a small dBASE program that imports the GetPassword function from PASSWORD.DLL:

LOAD DLL PASSWORD.DLL
EXTERN CLOGICAL GetPassword(CSTRING) PASSWORD.DLL

DO PWTEST.WFM

The file PWTEST.WFM in that program is a form that contains a single pushbutton with the following procedure:

Procedure PUSHBUTTON1_OnClick

IF GetPassword("PASSWORD")

   Form.ColorNormal = "G/G"

ELSE

   Form.ColorNormal = "R/R"

ENDIF


Opening the dialog box from a C/C++ application

Section topics 	See also

To import an interface routine into a C or C++ application:

1.	Declare the function name in the program's main source code. For example,

extern BOOL GetPassword(LPSTR);

2.	Run the IMPLIB utility on PASSWORD.DLL and add the resulting .LIB file to your project. Compiling the project then automatically includes the imported function.

You can then call the imported function from your application. For example, create a C++ application with the ObjectWindows library that consists of a form that contains a button. In response to the user pressing a button, call the following method that calls GetPassword:

void TTestWindow::ButtonPressed()

{

 if (GetPassword("PASSWORD"))

   SetBkgndColor(TColor::LtGreen);

 else

   SetBkgndColor(TColor::LtRed);

 Invalidate();

}


See also

TForm


Creating Internet server applications

Section topics 	See also

Delphi Client/Server enables you to create Web server applications as CGI applications or Dynamic Link Libraries (DLLs) that can contain any non-visual component. Internet-specific components make it easy to create event handlers that are associated with a specific Uniform Resource Identifier (URI) and, when processing is complete, to programmatically construct HTML documents and transfer them to the client.

Frequently, this content is drawn from databases. Delphi Internet components can be used to automatically manage connections to databases, allowing a single DLL to handle numerous simultaneous, thread-safe database connections.

This topic describes these Internet components, and discusses the creation of several types of Internet applications. You can also use ActiveForms as internet server applications. For more information about ActiveForms, see "Creating ActiveForms."


Terminology and standards

Section topics 	See also

Many of the protocols that control activity on the Internet are defined in Request for Comment (RFC) documents that are created, updated, and maintained by the Internet Engineering Task Force (IETF), the protocol engineering and development arm of the Internet. There are several important RFCs that you will find useful when writing Internet applications:

RFC822, "Standard for the format of ARPA Internet text messages," describes the structure and content of message headers

RFC1521, "MIME (Multipurpose Internet Mail Extensions) Part One: Mechanisms for Specifying and Describing the Format of Internet Message Bodies," describes the method used to encapsulate and transport multipart and multiformat messages.

RFC1945, "Hypertext Transfer Protocol -- HTTP/1.0," describes a transfer mechanism used to distribute collaborative hypermedia documents.

The IETF maintains a library of the RFCs on their Web site, www.ietf.cnri.reston.va.us.


Parts of a Uniform Resource Locator

Section topics 	See also

The Uniform Resource Locator (URL) is a complete description of the location of a resource that is available over the net. It is composed of several parts that may be accessed by an application.

Parts of a Uniform Resource Locator

URI vs. URL

The URL is a subset of the Uniform Resource Identifier (URI) defined in the HTTP standard, RFC1945. Web server applications frequently produce content from many sources where the final result does not reside in a particular location, but is created as necessary. URIs can describe resources that are not location specific.


An overview of HTTP server activity

Section topics 	See also

The client/server nature of web browsers is deceptively simple. To most users, retrieving information on the World Wide Web is a simple procedure: click on a link, and the information appears on the screen. More knowledgeable users have some understanding of the nature of HTML syntax and the client/server nature of the protocols used. This is usually sufficient for the production of simple, page-oriented web site content. Authors of more complex Web pages have a wide variety of options to automate the collection and presentation of information using HTML.

Before we examine the Delphi components that help you collect and present HTML documents, let's examine how the client issues a request and how the server deals with those requests in more detail, specifically:

How a client composes a request

What a Web server does when it receives a request

How page-creation programs receive information from the Web server

How the results are passed back to the Web server


Composing client requests

Section topics 	See also

When an HTML hypertext link is selected (or the user otherwise specifies a URL), the browser collects information about the protocol, the specified domain, the path to the information, the date and time, the operating environment, the browser itself, and other content information. It then composes a request.

For example, to display a page of images based on criteria selected by clicking buttons on a form, the client might construct this URL:

http://www.TSite.com/art/gallery.dll/animals?animal=dog&color=black

which specifies an HTTP server in the www.TSite.com domain. The client contacts www.TSite.com, connects to the HTTP server, and passes it a request. The request might look something like this:

GET /art/gallery.dll/animals?animal=dog&color=black HTTP/1.0

Connection: Keep-Alive

User-Agent: Mozilla/3.0b4Gold (WinNT; I)

Host: www.TSite.com:1024

Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*


Serving client requests

Section topics 	See also

The server receives this request and might perform any number of actions, based on the server's configuration. If the server is configured to recognize the /gallery.dll portion of the request as a program, the way information about the request is passed to the program depends on the type of program:

If the program is a Common Gateway Interface (CGI) program, the server passes the information contained in the request directly to the CGI program. The server waits while the program executes. When the CGI program exits, it passes the content directly back to the server.

If the program is Win-CGI, the server opens a file, writes out the request information, and executes the Win-CGI program. The server also passes the location of the file containing the client information and the location of a file that the Win-CGI program should use to create content. The server waits while the program executes. When the program exits, the server reads the data from the content file written by the Win-CGI program.

If the program is a Dynamic Link Library (DLL), the server loads the DLL (if necessary) and passes the information contained in the request to the DLL as a structure. The server waits while the program executes. When the DLL exits, it passes the content directly back to the server.

In all cases, the program acts on the request and performs actions specified by the programmer: accessing databases, doing simple table lookups or calculations, constructing or selecting HTML documents, and so on.


Responding to client requests

Section topics 	See also

When each program finishes with a client request, it constructs a page of HTML code or other MIME content, and passes it back (via the server) to the client for display. The way the response is sent also differs based on the type of program:

When a Win-CGI script finishes it constructs a page of HTML, writes it to a file, writes any response information to another file, and passes the locations of both files back to the server. The server opens both files and passes the HTML page back to the client.

When a DLL finishes, it passes the HTML page and any response information directly back to the server, which passes them back to the client.

Creating a web server application as a DLL reduces system load and resource use by reducing the number of processes and disk accesses necessary to service an individual request.


Web server applications

Section topics 	See also

Web server applications extend the functionality and capability of existing Web servers. The Web server application receives HTTP request messages from the Web server, performs any actions requested in those messages, and formulates responses that it passes back to the Web server. Any operation that you can perform with a Delphi application can be incorporated into a Web server application.

Types of Web server applications

You can use Delphi to create four types of Web server applications. Each type uses a type-specific descendant of TWebApplication, TWebRequest, and TWebResponse:

Application Type	Application Object	Request Object	Response Object
Microsoft Server DLL (ISAPI)	TISAPIApplication	TISAPIRequest	TISAPIResponse
Netscape Server DLL (NSAPI)	TISAPIApplication	TISAPIRequest	TISAPIResponse
Console CGI application	TCGIApplication	TCGIRequest	TCGIResponse
Windows CGI application	TCGIApplication	TWinCGIRequest	TWinCGIResponse
ISAPI and NSAPI

An ISAPI or NSAPI Web server application is a DLL that is loaded by the Web server. Client request information is passed to the DLL as a structure and evaluated by TISAPIApplication, which creates the dispatcher, TISAPIRequest and TISAPIResponse objects.  Each request message is handled in a separate execution thread.

CGI standalone

A CGI standalone Web server application is a console application that receives client request information on standard input and passes the results back to the server on standard output. This data is evaluated by TCGIApplication, which creates the dispatcher, TCGIRequest and TCGIResponse objects.  Each request message is handled by a separate instance of the application.

WinCGI standalone

A Win-CGI standalone Web server application is a Windows application that receives client request information from a configuration settings (INI) file written by the server and writes the results to a file that the server passes back to the client. The INI file is evaluated by TCGIApplication, which creates the dispatcher, TWinCGIRequest and TWinCGIResponse objects.  Each request message is handled by a separate instance of the application.


Creating Web server applications

Section topics 	See also

All new Delphi Web server applications are created by selecting File|New from the menu of the Delphi main window and selecting Web Server Application in the New Items dialog. A dialog box appears, where you can select one of the Web server application types:

ISAPI and NSAPI	Selecting this type of application adds the library header of the project files and required entries to the uses list and exports clause of the project file.
CGI standalone	Selecting this type of application adds the required entries to the uses clause of the project file and adds the appropriate $APPTYPE directive to the source.
Win-CGI standalone	Selecting this type of application adds the required entries to the uses clause of the project file and adds the appropriate $APPTYPE directive to the source.

Choose the type of Web Server Application that communicates with the type of Web Server your application will use. This creates a new project configured to use Internet components and containing an empty Web module.

The Web module is a descendant of TDataModule and may be used in the same way: to provide centralized control for business rules and non-visual components in the Web application.

Warning:	The Web module in a DLL-based Web server application is cached for later reuse to increase response time. The state of the dispatcher and the action list is not reinitialized between requests. Changing the Enabled or Default property of an action item during execution may cause unexpected results when that module is used for subsequent client requests.


Setting up the Web module

Section topics 	See also

The Web module acts as a repository for non-visual components and business rules that you want to use in your application. Add any content producers that your application will use to generate response messages. These can be the content producers provided by Delphi (such as TPageProducer, TDataSetTableProducer, and TQueryTableProducer), or descendants of TCustomContentProducer that you have written yourself. Add any helper objects that describe the content of responses, such as THTMLTableAttributes or THTMLTableCellAttributes. If your application generates response messages that include material drawn from databases, add data access components such as TSession, TDatabase, TTable, TQuery, TStoredProc, or TBatchMove.

In addition to storing non-visual components and business rules, the Web module also acts as a dispatcher, matching incoming HTTP request messages to action items that generate the responses to those requests.

You may have a data module already that is set up with many of the non-visual components and business rules that you want to use in your Web application. You can replace the Web module with your pre-existing data module. Simply delete the automatically generated Web module and replace it with your data module. Then, add a TWebDispatcher component to your data module, so that it can dispatch request messages to action items, the way a Web module can. If you want to change the way action items are chosen to respond to incoming HTTP request messages, derive a new dispatcher component from TCustomWebDispatcher, and add that to the data module instead.

Your project can contain only one dispatcher. This can either be the Web module that is automatically generated when you create the project, or the TWebDispatcher component that you add to a data module that replaces the Web module. If a second data module containing a dispatcher is created during execution, the Web server application will generate a runtime error.

Note:	The Web module that you set up at design time is actually a template. In ISAPI and NSAPI applications, each request message spawns a separate thread, and separate instances of the Web module and its contents are created dynamically for each thread.


The Web Application object

Section topics 	See also

The project that is set up for your Web application contains a global variable named Application. Application is a descendant of TWebApplication (either TISAPIApplication or TCGIApplication) that is appropriate to the type of application you are creating. It runs in response to HTTP request messages received by the Web server.

Warning:	Do not include the Forms unit after the CGIApp or ISAPIApp unit in the uses list of the project file. The Forms unit also declares a global variable named Application, and if it appears after the CGIApp or ISAPIApp unit, Application will be initialized to an object of the wrong type.


The structure of a Web server application

Section topics 	See also

When the Web application receives an HTTP request message, it creates a TWebRequest object to represent the HTTP request message, and a TWebResponse object to represent the response that should be generated. The application then passes these objects to the Web dispatcher (either the Web module or a TWebDispatcher component).

The Web dispatcher controls the flow of a Delphi Web server application. The dispatcher maintains a collection of action items (TWebActionItem) that know how to handle certain types of HTTP request messages. The dispatcher identifies the appropriate action items to handle the HTTP request message, and triggers the event handler of the action items so that they can perform any requested actions or formulate a response message. It is described more fully in "The Web dispatcher."

Structure of a Server Application

Code in the action item event handlers has access to all request information as properties of the TWebRequest object. Specialized content producer components (like TPageProducer and TDataSetTableProducer) allow the action items to dynamically produce custom HTML code or other MIME content. The content producers make use of other content producers, or descendants of THTMLTagAttributes, to help them create the content of the response message. The action item event handlers use this content to fill out the emerging request message. For more information on content producers, see "Generating the content of response messages."

When all action items have finished creating the response by filling out the TWebResponse object, the dispatcher passes the result back to the Web application. The application sends the response on to the client via the Web server.


The Web dispatcher

Section topics 	See also

If you are using a Web module, it acts as a Web dispatcher. If you are using a pre-existing data module, you must add a single dispatcher component to that data module. The dispatcher maintains a collection of action items that know how to handle certain kinds of request messages. When the Web application passes a request object and a response object to the dispatcher, it chooses one or more action items to respond to the request.


Adding actions to the dispatcher

Section topics 	See also

You can open the action editor from the Object Inspector by clicking the ellipses on the Actions property of the dispatcher. Actions can be added to the dispatcher by clicking the Add button in the action editor.

Add actions to the dispatcher to respond to different request methods or target URIs. You can set up your action items in a variety of ways. You can start with action items that preprocess requests, and end with a default action that checks whether the response is complete and either sends the response or returns an error code. Or, you can add a separate action item for every type of request, where each action item completely handles the request.

Action items are discussed in further detail in "Action items."


Dispatching request messages

Section topics 	See also

When the dispatcher receives the client request, it generates a BeforeDispatch event. This provides your application with a chance to preprocess the request message before it is seen by any of the action items.

After the BeforeDispatch event, the dispatcher looks through its list of action items for one that matches the target URL of the HTTP request message and that indicates it can provide the service specified as the method of the request message. It does this by comparing the PathInfo and MethodType properties of the TWebRequest object with the properties of the same name on the action item.

When the dispatcher finds an appropriate action item, it triggers the OnAction event handler of the action item. The OnAction event handler can completely handle the request, partially handle the request and allow other action items to complete the job, or completely defer the request to other action items. The OnAction event handler signals that the dispatcher should continue passing the request to other action items by setting the Handled parameter to False.

If the dispatcher checks all of its action items, and the request message has still not been fully handled, it calls the default action item. The default action item does not need to match either the target URL or the method of the request.

When the dispatcher reaches the end of the action list (including the default action, if any) and no actions have been triggered, nothing is passed back to the server. The server will simply drop the connection to the client.

If the request was handled by the action items, the dispatcher generates an AfterDispatch event. This provides a final opportunity for your application to check the response that was generated, and make any last minute changes.


Action items

Section topics 	See also

Each action item performs a specific task in response to a given type of request message. Action items can completely respond to a request or perform part of the response and allow other action items to complete the job. Action items can send the HTTP response message for the request, or simply set up part of the response for other action items to complete. If a response is completed by the action items but not sent, the Web server application will send the response message.


Specifying action item properties

Section topics 	See also

The properties of the action item determine whether the dispatcher selects it to handle an HTTP request message. To set the properties of an action item, you must first bring up the action editor: select the Actions property of the dispatcher in the Object Inspector and click on the ellipses. When an action is selected in the action editor, its properties can be modified in the Object Inspector.

The target URL

The dispatcher compares the PathInfo property of an action item to the path portion of the target URL of the request message. The value of this property should be the path information portion of the URL for all requests the action item is prepared to handle. For example, given this URL,

http://www.TSite.com/art/gallery.dll/mammals?animal=dog&color=black

and assuming that the /gallery.dll part is the Web server application, the path information portion is

/mammals

The request method

The MethodType property of an action item indicates what type of request messages it can process. The dispatcher compares the MethodType property of an action item to the MethodType of the request message. MethodType can take one of the following values:

Value	Meaning
mtGet	The request is asking for the information associated with the target URI to be returned in a response message.
mtHead	The request is asking for the header properties of a response, as if servicing an mtGet request, but omitting the content of the response.
mtPost	The request is providing information to be posted to the Web application.
mtPut	The request asks that the resource associated with the target URI be replaced by the content of the request message.
mtAny	The action item matches any request method type, including mtGet, mtHead, and mtPost.
Enabling and disabling action items

Each action item has an Enabled property that can be used to enable or disable that action item. By setting Enabled to False, you disable the action item so that it is not considered by the dispatcher when it looks for an action item to handle a request.

A BeforeDispatch event handler can control which action items should process a request by changing the Enabled property of the action items before the dispatcher begins matching them to the request message.

Caution:	Changing the Enabled property of an action during execution may cause unexpected results for subsequent requests. If the Web server application is a DLL that caches web modules, the initial state will not be reinitialized for the next request. Use the BeforeDispatch event to ensure that all action items are correctly initialized to their appropriate starting states.

Choosing a default action item

Only one of the action items can be the default action item. The default action item is selected by setting its Default property to True. When the Default property of an action item is set to True, the Default property for the previous default action item (if any) is set to False.

When the dispatcher searches its list of action items to choose one to handle a request, it stores the name of the default action item. If the request has not been fully handled when the dispatcher reaches the end of its list of action items, it calls the OnAction event handler of the default action item.

If the action item is the default and the request has not been handled, the dispatcher calls its event handler. The dispatcher does not check the PathInfo or MethodType of the default action item. The dispatcher does not even check the Enabled property of the default action item. Thus, you can make sure the default action item is called only at the very end by setting its Enabled property to False.

The default action item should be prepared to handle any request that is encountered, even if it is only to return an error code indicating an invalid URI or MethodType. If the default action item does not handle the request, no response will be sent to the Web client.

Caution:	Changing the Default property of an action during execution may cause unexpected results for the current request. If the Default property of an action that has already been triggered is set to True, that action will not be re-evaluated and the dispatcher will not trigger that action when it reaches the end of the action list.


Responding to request messages with action items

Section topics 	See also

An action item responds to a request message in its OnAction event handler. This is where the real work of the Web server application is done. The OnAction event handler is passed the TWebRequest object that represents the HTTP request message and a TWebResponse object to fill out with any response information.

In a OnAction event handler, you can use any of the Delphi objects or runtime library methods to respond to request messages. You can access databases, perform calculations, construct or select HTML documents, and so on. Delphi provides you with a number of content-producer objects that can help construct an HTML page to set as the content of the response message.

Sending the response

The action item can send the response back to the Web client by using the methods of the TWebResponse object. However, if no action item sends the response to the client, it will still get sent by the Web server application as long as the last action item to look at the request indicates that the request was handled.

Using multiple action items

You can respond to a request from within a single OnAction event handler, or divide the work up among several action items. After each action item has done its own particular task, it can signal to the dispatcher that the response is not finished by setting the Handled parameter of the OnAction event handler to False.

If many action items divide up the work of responding to request messages, each setting Handled to False so that others can continue, make sure the default action item leaves the Handled parameter set to True. Otherwise, no response will be sent to the Web client.

When dividing the work among several action items, either the OnAction event handler of the default action item or the AfterDispatch event handler of the dispatcher should check whether all the work was done and set an appropriate error code if it is not.


Accessing client request information

Section topics 	See also

When an HTTP request message is received by the Web server application, the parameters of the client request are loaded into the properties of a TWebRequest object. In NSAPI and ISAPI applications, the request message is encapsulated by a TISAPIRequest object. Console CGI applications use TCGIRequest objects, and Windows CGI applications use TWinCGIRequest objects.

The properties of the request object are read-only. You can use them to gather all of the information available in the client request.


Properties that contain request header information

Section topics 	See also

Most properties in a request object contain information about the request that comes from the HTTP request header. Not every request supplies a value for every one of these properties. Also, some requests may include header fields that are not surfaced in a property of the request object, especially as the HTTP standard continues to evolve. To obtain the value of a request header field that is not surfaced as one of the properties of the request object, use the GetFieldByName method.

Properties that identify the target

The full target of the request message is given by the URL property. Usually, this is a URL that can be broken down into the protocol (HTTP), Host (server system), ScriptName (server application), PathInfo (location on the host), and Query. Each of these pieces is surfaced in its own property. The dispatcher uses the PathInfo portion when matching action items to request messages. The protocol is always HTTP, and the Host and ScriptName identify the Web server application. The Query is used by some requests to specify the details of the requested information. Its value is also parsed for you as the QueryFields property.

Properties that identify the Web client

The request also includes several properties that provide information about where the request originated. These include everything from the e-mail address of the sender (the From property), to the URI where the message originated (the Referer or RemoteHost property). If the request contains any content, and that content does not arise from the same URI as the request, the source of the content is given by the DerivedFrom property. You can also determine the IP address of the client (the RemoteAddr property), and the name and version of the application that sent the request (the UserAgent property).

Properties that identify the purpose of the request

The Method property is a string describing what the request message is asking the server application to do. The HTTP 1.1 standard defines the following methods:

Value	What the message requests
OPTIONS	Information about available communication options.
GET	Information identified by the URL property.
HEAD	Header information from an equivalent GET message, without the content of the response.
POST	The server application to post the data included in the Content property, as appropriate.
PUT	The server application to replace the resource indicated by the URL property with the data included in the Content property.
DELETE	The server application to delete or hide the resource identified by the URL property.
TRACE	The server application to send a loop-back to confirm receipt of the request.

The Method property may indicate any other method that the Web client requests of the server.

The Web server application does not need to provide a response for every possible value of Method. The HTTP standard does require that it service both GET and HEAD requests, however.

The MethodType property indicates whether the value of Method is GET (mtGet), HEAD (mtHead), POST (mtPost), PUT (mtPut) or some other string (mtAny). The dispatcher matches the value of the MethodType property with the MethodType of each action item.

Properties that describe the expected response

The Accept property indicates the media types the Web client will accept as the content of the response message. The IfModifiedSince property specifies whether the client wants only information that has changed recently. The Cookie property includes state information that can modify the response.

Properties that describe the content

Most requests do not include any content, as they are requests for information. However, some requests, such as POST requests, provide content that the Web server application is expected to use. The media type of the content is given in the ContentType property, and its length in the ContentLength property. If the content of the message was encoded (for example, for data compression), this information is in the ContentEncoding property. The name and version number of the application that produced the content is specified by the ContentVersion property. The Title property may also provide information about the content.


The content of HTTP request messages

Section topics 	See also

In addition to the header fields, some request messages include a content portion that the Web server application should process in some way. For example, a POST request might include information that should be added to a database maintained by the Web server application.

The unprocessed value of the content is given by the Content property. If the content can be parsed into fields separated by ampersands (&), a parsed version is available in the ContentFields property.


Creating HTTP response messages

Section topics 	See also

When the Web server application creates a TWebRequest object for an incoming HTTP request message, it also creates a corresponding TWebResponse object to represent the response message that will be sent in return. In NSAPI and ISAPI applications, the response message is encapsulated by a TISAPIResponse object. Console CGI applications use TCGIResponse objects, and Windows CGI applications use TWinCGIResponse objects.

The OnAction event handlers that generate the response to a Web client request fill in the properties of the response object. In some cases, this may be as simple as returning an error code or redirecting the request to another URI. In other cases, this may involve complicated calculations that require the event handler to fetch information from other sources and assemble it into a finished form. Most request messages require some response, even if it is only the acknowledgment that a requested action was carried out.


Filling in the response header

Section topics 	See also

Most of the properties of the TWebResponse object represent the header information of the HTTP response message that is sent back to the web client. Not every response message needs to specify a value for every one of the header properties. The properties that should be set depend on the nature of the request and the status of the response.

Indicating the response status

Every response message must include a status code that indicates the status of the response. You can specify the status code by setting the StatusCode property. The HTTP standard defines a number of standard status codes with predefined meanings. In addition, you can define your own status codes using any of the unused possible values.

Each status code is a three-digit number where the most significant digit indicates the class of the response, as follows:

1xx: Informational (The request was received but has not been fully processed).

2xx: Success (The request was received, understood, and accepted).

3xx: Redirection (Further action by the client is needed to complete the request).

4xx: Client Error (The request cannot be understood or cannot be serviced).

5xx: Server Error (The request was valid but the server could not handle it).

Associated with each status code is a string that explains the meaning of the status code. This is given by the ReasonString property. For predefined status codes, you do not need to set the ReasonString property. If you define your own status codes, you should also set the ReasonString property.

Indicating the need for client action

When the status code is in the 300-399 range, the client must perform further action before the Web server application can complete the request. If you need to redirect the client to another URI, or indicate that a new URI was created to handle the request, set the Location property. If the client must provide a password before you can proceed, set the WWWAuthenticate property.

Describing the server application

Some of the response header properties describe the capabilities of the Web server application. The Allow property indicates the methods to which the application can respond. The Server property gives the name and version number of the application used to generate the response.

Describing the content

Several properties describe the contents of the response. ContentType gives the media type of the response, and ContentVersion is the version number for that media type. ContentLength gives the length of the response. If the content is encoded (such as for data compression), indicate this with the ContentEncoding property. If the content came from another URI, this should be indicated in the DerivedFrom property. If the value of the content is time-sensitive, the LastModified property and the Expires property indicate whether the value is still valid. The SetCookie and Title properties can provide state or descriptive information about the content.


Setting the response content

Section topics 	See also

For some requests, the response to the request message is entirely contained in the header properties of the response. In most cases, however, the final action of the event handler should be to assign some content to the response message. This content may be static information stored in a file, or information that was dynamically produced by the OnAction event handlers.

You can set the content of the response message by using either the Content property or the ContentStream property.

The Content property is a Delphi string. Delphi strings are not limited to text values, so the value of the Content property can be a string of HTML commands, graphics content such as a bit-stream, or any other MIME content type.

Use the ContentStream property if the content for the response message can be read from a stream. For example, if the response message should send the contents of a file, use a TFileStream object for the ContentStream property. As with the Content property, ContentStream should provide a string of HTML commands or other MIME content type. If you use the ContentStream property, do not free the stream yourself. The Web response object automatically frees it for you.

Note:	If the value of the ContentStream property is not nil, the Content property is ignored.


Sending the response

Section topics 	See also

If you are sure there is no more work to be done in response to a request message, you can send a response directly from the OnAction event handler. The TWebResponse object provides two methods for sending a response: SendResponse and SendRedirect. Call SendResponse to send the response using the specified content and all the header properties of the TWebResponse object. If you only need to redirect the web client to another URI, the SendRedirect method is more efficient.

If none of the event handlers send the response, the Web application object sends it after the dispatcher finishes. However, if none of the action items indicate that they have handled the response, the application will close the connection to the web client without sending any response.


Generating the content of response messages

Section topics 	See also

Delphi provides a number of objects to assist your OnAction event handlers in producing content for HTTP response messages. You can use these objects to generate strings of HTML commands that are saved in a file or sent directly back to the Web client. You can write your own content producers, deriving them from TCustomContentProducer or one of its descendants.

TCustomContentProducer provides a generic interface for creating any MIME type as the content of an HTTP response message. It has two descendants, TDSTableProducer and TCustomPageProducer. TDSTableProducer produces HTML commands based on the information in a dataset. Its descendants are described in "Using database information in responses."

TCustomPageProducer is the base class for TPageProducer. Page producers scan HTML documents for special tags that they replace with customized HTML code.


Using page producer components

Section topics 	See also

The TPageProducer component takes an HTML template and converts it by replacing special HTML-transparent tags with customized HTML commands. You can store a set of standard response templates that are filled in by page producers when you need to generate the response to an HTTP request message. You can chain page producers together to iteratively build up an HTML document by successive refinement of the HTML-transparent tags.

HTML templates

An HTML template is a sequence of HTML commands and HTML-transparent tags. An HTML-transparent tag has the form

<#TagName Param1=Value1 Param2=Value2 ...>

The angle brackets (< and > ) define the entire scope of the tag. A pound sign (#) immediately follows the opening angle bracket (<) with no spaces separating it from the angle bracket. The pound sign identifies the string to the page producer as an HTML-transparent tag. The tag name immediately follows the pound sign with no spaces separating it from the pound sign. The tag name can be any valid Object Pascal identifier and identifies the type of conversion the tag represents.

Delphi provides several predefined tag names that are associated with values of the TTag data type.

Tag Name	TTag value	What the tag should be converted to
Link	tgLink	A hypertext link. The result is an HTML sequence beginning with an <A> tag and ending with an </A> tag.
Image	tgImage	A graphic image. The result is an HTML <IMG> tag.
Table	tgTable	An HTML table. The result is an HTML sequence beginning with a <TABLE> tag and ending with a </TABLE> tag.
ImageMap	tgImageMap	A graphic image with associated hotzones. The result is an HTML sequence beginning with a <MAP> tag and ending with a </MAP> tag.
Object	tgObject	An embedded ActiveX object. The result is an HTML sequence beginning with an <OBJECT> tag and ending with an </OBJECT> tag.
Embed	tgEmbed	A Netscape-compliant add-in DLL. The result is an HTML sequence beginning with an <EMBED> tag and ending with an </EMBED> tag.

Any other tag name will be associated with tgCustom. The page producer supplies no built-in processing of the predefined tag names. They are simply provided to help applications organize the conversion process into many of the more common tasks.

Note:	The predefined tag names are case insensitive.

Following the tag name, the HTML-transparent tag can optionally include parameters that specify details of the conversion to be performed. Each parameter is of the form ParamName=Value, where there is no space between the parameter name, the equals symbol (=) and the value. The parameters are separated by whitespace.

The angle brackets (< and >) make the tag transparent to HTML browsers that do not recognize the #TagName construct.

Specifying the HTML template

TPageProducer provides you with many choices in how to specify the HTML template. You can set the HTMLFile property to the name of a file that contains the HTML template. You can set the HTMLDoc property to a TStrings object that contains the HTML template. If you use either the HTMLFile property or the HTMLDoc property to specify the template, you can generate the converted HTML commands by calling the Content method.

In addition, you can call the ContentFromString method to directly convert an HTML template that is a single Delphi string which is passed in as a parameter. Or you can call the ContentFromStream method to read the HTML template from a stream. Thus, for example, you could store all your HTML templates in a memo field in a database, and use the ContentFromStream method to obtain the converted HTML commands, reading the template directly from a TBlobStream object.

Converting HTML-transparent tags

The page producer converts the HTML template when you call one of its Content methods. When the Content method encounters an HTML-transparent tag, it triggers the OnHTMLTag event handler. You must write an event handler to determine the type of tag encountered, and to replace it with customized content.

Using page producers from an action item

A typical use of a page producer component would use the HTMLFile property to specify a file containing an HTML template and call the Content method to convert the template into a final HTML sequence:

procedure MyActionEventHandler(Sender: TObject; Request: TWebRequest;

                              Response: TWebResponse; var Handled: Boolean);

begin

 PageProducer1.HTMLFile := 'Greeting.html';

 Response.Content := PageProducer1.Content;

end;

Greeting.html is a file that contains this HTML template:

<HTML>

<HEAD><TITLE>Our brand new web site</TITLE></HEAD>

<BODY>

Hello <#UserName>!  Welcome to our web site.

</BODY>

</HTML>

The OnHTMLTag event handler replaces the custom tag (<#UserName>) in the HTML during execution:

procedure HTMLTagHandler(Sender: TObject;Tag: TTag;

                        constTagString: String; TagParams: TStrings;

                        var ReplaceText: String);

begin

 if CompareText(TagString,'UserName') = 0 then

   ReplaceText := TPageProducer(Sender).Dispatcher.Request.Content;

end;

If the content of the request message was the string Mr. Ed, the value of Response.Content would be

<HTML>

<HEAD><TITLE>Our brand new web site</TITLE></HEAD>

<BODY>

Hello Mr. Ed!  Welcome to our web site.

</BODY>

</HTML>

If you have not created an OnHTMLTag event handler for the TPageProducer object, the tag is replaced with an empty string.

Chaining page producers together

The replacement text from an OnHTMLTag event handler need not be the final HTML sequence you want to use in the HTTP response message. You may want to use several page producers, where the output from one page producer is the input for the next.

For example, consider an application that returns calendar pages in response to request messages that specify the month and year of the desired page. Each calendar page contains a picture, followed by the name and year of the month between small images of the previous month and next months, followed by the actual calendar. The resulting image looks something like this:

The general form of the calendar is stored in a template file. It looks like this:

<HTML>

<Head></HEAD>

<BODY>

<#MonthlyImage> <#TitleLine><#MainBody>

</BODY>

</HTML>

The OnHTMLTag event handler of the first page producer looks up the month and year from the request message. Using that information and the template file, it does the following:

replaces <#MonthlyImage> with <#Image Month=January Year=1997>.

replaces <#TitleLine> with <#Calendar Month=December Year=1996 Size=Small> January 1997 <#Calendar Month=February Year=1997 Size=Small>.

replaces <#MainBody> with <#Calendar Month=January Year=1997 Size=Large>.

The OnHTMLTag event handler of the next page producer uses the content produced by the first page producer, and replaces the <#Image Month=January Year=1997> tag with the appropriate HTML <IMG> tag.Yet another page producer resolves the #Calendar tags with appropriate HTML tables.


Using database information in responses

Section topics 	See also

The response to an HTTP request message may include information taken from a database. Delphi provides specialized content producers that can generate the HTML to represent the records from a database in an HTML table.


Adding a session to the Web module

Section topics 	See also

Both console CGI applications and Win-CGI applications are launched in response to HTTP request messages. When working with databases in these types of applications, you can use the default session to manage your database connections, because each request message has its own instance of the application. Each instance of the application has its own distinct, default session.

When writing an ISAPI application or an NSAPI application, however, each request message is handled in a separate thread of a single application instance. To prevent the database connections from different threads from interfering with each other, you must give each thread its own session.

Each request message in an ISAPI or NSAPI application spawns a new thread. The Web module for that thread is generated dynamically at runtime. Add a TSession object to the Web module to handle the database connections for the thread that contains the Web module.

Separate instances of the Web module are generated for each thread at runtime. Each of those modules will contain the session object. Each of those sessions must have a separate name, so that the threads that handle separate request messages do not interfere with each other's database connections. To cause the session objects in each module to dynamically generate unique names for themselves, set the AutoSession property of the session object. Each session object will dynamically generate a unique name for itself and set the AutoSessionName property of all datasets in the module to refer to that unique name. This allows all interaction with the database for each request thread to proceed without interfering with any of the other request messages. For more information on sessions, see "Managing database sessions."


Representing a dataset in HTML

Section topics 	See also

Descendants of TDSTableProducer format the information from a dataset as an HTML table. You can customize the appearance of the table by specifying properties for the table's color, border, separator thickness, and so on.

Specifying the table attributes

TDSTableProducer objects use the THTMLTableAttributes object to describe the visual appearance of the HTML table that displays the records from the dataset. The THTMLTableAttributes object includes properties for the table's width and spacing within the HTML document, and for its background color, border thickness, cell padding, and cell spacing. These properties are all turned into options on the HTML <TABLE> tag created by the TDSTableProducer object.

At design time, specify these properties using the Object Inspector. Select the table producer object in the Object Inspector and expand the TableAttributes property to access the display properties of the THTMLTableAttributes object.

You can also specify these properties programmatically at runtime.

Specifying the row attributes

Similar to the table attributes, you can specify the alignment and background color of the cells for the rows of the table that display data. The RowAttributes property is a THTMLTableRowAttributes object.

At design time, specify these properties using the Object Inspector by expanding the RowAttributes property. You can also specify these properties programmatically at runtime.

You can also adjust the number of rows shown in the HTML table by setting the MaxRows property.

Specifying the columns

If you know the dataset for the table at design time, you can use the Columns editor to customize the columns' field bindings and display attributes. Select the table producer component, and right-click. From the context menu, choose the Columns editor. This lets you add, delete, or rearrange the columns in the table. You can set the field bindings and display properties of individual columns in the Object Inspector after selecting them in the Columns editor.

If you are getting the name of the dataset from the HTTP request message, you can't bind the fields in the Columns editor at design time. However, you can still set up the columns programmatically at runtime, by setting up the appropriate THTMLTableColumn objects and using the methods of the Columns property to add them to the table. If you do not set up the Columns properly, Delphi creates a default set of columns that match the fields of the dataset and specify no special display characteristics.

Embedding tables in HTML documents

You can embed the HTML table that represents your dataset in a larger document by using the Header and Footer properties of the TDSTableProducer component. Use Header to specify everything that comes before the table, and Footer to specify everything that comes after the table.

You may want to use another content producer to create the values for the Header and Footer properties.

If you embed your table in a larger document, you may want to add a caption to the table. Use the Caption and CaptionAlignment properties to give your table a caption.


Using TDataSetTableProducer

Section topics 	See also

TDataSetTableProducer is a TDSTableProducer component that creates an HTML table for a dataset. Set the DataSet property of TDataSetTableProducer to specify the dataset that contains the records you want to display. You do not set the DataSource property, as you would for most data-aware objects in a conventional Delphi application. This is because TDataSetTableProducer generates its own data source internally. As an HTML image, the table produced by TDataSetTableProducer does not need to maintain record currency with other objects.

You can set the value of DataSet at design time if your web application always displays records from the same dataset. You must set the DataSet property at runtime if you are basing the dataset on the information in the HTTP request message.


Using TQueryTableProducer

Section topics 	See also

You can produce an HTML table to display the results of a query, where the parameters of the query come from the HTTP request message. Specify the TQuery object that uses those parameters as the Query property of a TQueryTableProducer component.

If the request message is a GET request, the parameters of the query come from the Query fields of the URL that was given as the target of the HTTP request message. If the request message is a POST request, the parameters of the query come from the content of the request message.

When you call the Content method of TQueryTableProducer, it runs the query, using the parameters it finds in the TWebRequest object. It then formats an HTML table to display the records in the resulting dataset.

As with any TDSTableProducer component, you can customize the display properties or column bindings of the HTML table, or embed the table in a larger HTML document.


Debugging server applications

Section topics 	See also

Debugging Web server applications presents some unique problems, because they run in response to messages from a Web server. You can not simply launch your application from the IDE, because that leaves the Web server out of the loop, and your application will not find the request message it is expecting. These topics explains how you can debug your Web server application.


Debugging ISAPI and NSAPI applications

Section topics 	See also

ISAPI and NSAPI applications are actually DLLs that contain predefined entry points. The Web server passes request messages to the application by making calls to these entry points. You will need to set your application's run parameters to launch the server. Set up your breakpoints so that when the server passes a request message to your DLL, you hit one of your breakpoints, and can debug normally.

Note:	Before launching the Web server using your application's run parameters, make sure that the server is not already running.

Debugging under Windows NT

Under Windows NT, you must have the correct user rights to debug a DLL. In the User Manager:

1.	Choose Policies|User Rights.

2.	Select Show Advanced User Rights.

3.	Choose Act As Part Of The Operating System in the Rights list.

4.	Add your name to the Grant To list.

Debugging with a Microsoft IIS server

To debug a Web server application using Microsoft IIS server, choose Run | Parameters and set your application's run parameters:

   Host Application: c:\winnt\system32\inetsrv\inetinfo.exe

   Run Parameters:   -e w3svc

This starts the IIS Server and allows you to debug your ISAPI DLL.

Note:	Your directory path for inetinfo.exe may differ from the example.

Debugging with a Windows 95 Personal Web Server

To debug a Web server application using Personal Web Server, set your application's run parameters:

   Host Application: c:\Program Files\websvc\system\inetsw95.exe

   Run Parameters:   -w3svc

This starts the Personal Web Server and allows you to debug your ISAPI DLL.

Note:	Your directory path for inetsw95.exe may differ from the example.

Debugging with Netscape Server Version 2.0

Before using Web server applications on Netscape servers, you must make certain configuration changes.

First, copy the ISAPITER.DLL file (from Delphi 3.0\Bin) into the C:\Netscape\Server\Nsapi\Examples directory. (Your directory path may differ.)

Next, make the following modifications to the server configuration files located in the C:\Netscape\Server\Httpd-<servername>\Config directory.

1.	In the OBJ.CONF file, insert the line

  Init funcs="handle-isapi,check-isapi,log-isapi" fn="load-modules"

     shlib="c:/netscape/server/nsapi/examples/ISAPIter.dll"

after the line

  Init fn=load-types mime-types=mime.types

2.	In the <Object name=default> section of OBJ.CONF, insert the line

  NameTrans from="/scripts" fn="pfx2dir" dir="C:/Netscape/Server/docs/scripts"

     name="isapi"

before the line

  NameTrans fn=document-root root="C:/Netscape/Server/docs"

3.	Add the following section to the end of OBJ.CONF:

  <Object name="isapi">

  PathCheck fn="check-isapi"

  ObjectType fn="force-type" type="magnus-internal/isapi"

  Service fn="handle-isapi"

  </Object>

4.	Add the following line to the end of the MIME.TYPES file:

  type=magnus-internal/isapi exts=dll

This should be the last line in the file.

Note:	Line breaks are included in steps 1 and 2 above only to enhance readability. Do not type carriage-returns when you place these lines in configuration files.

To debug a Web server application using Netscape Fast Track server, set the application's run parameters as follows:

Host Application: c:\Netscape\server\bin\httpd\httpd.exe

Run Parameters:   c:\Netscape\server\httpd-<servername>\config

This starts the server and indicates to the server where the configuration files are located.


Debugging CGI and Win-CGI applications

Section topics 	See also

It is more difficult to debug CGI and Win-CGI applications, because the application itself must be launched by the web server.

Simulating the server

For Win-CGI applications, you can simulate the server by manually writing the configuration settings file that contains the request information. Then launch the Win-CGI application, passing the location of the file containing the client information and the location of a file that the Win-CGI program should use to create content. You can then debug normally.

Debugging as a DLL

Another approach you can take with both CGI and Win-CGI applications is first to create and debug your application as an ISAPI or NSAPI application. Once it is working smoothly, convert your application to a CGI or Win-CGI application.

CGI and Win-CGI applications are simpler than ISAPI and NSAPI applications. Each instance of a CGI or Win-CGI application must handle only a single thread. Thus, these applications do not encounter the multi-threading issues that ISAPI and NSAPI applications must deal with. They also are immune to the problems that can arise from the caching of Web modules in ISAPI and NSAPI applications.

To directly convert your application, you must edit the uses clause and exports clause of the project, remove the library header, and add the appropriate $APPTYPE directive. As an alternate approach, you can create a new CGI or Win-CGI application and copy everything from the Web module of the debugged application into the Web module of your new application.


See also

TISAPIApplication

TISAPIRequest

TISAPIResponse

TCGIApplication

TCGIRequest

TCGIResponse

TWinCGIResponse

TWinCGIRequest

TPageProducer

TCustomPageProducer

TDataSetTableProducer

TQueryTableProducer

TCustomContentProducer

THTMLTableAttributes

THTMLTableCellAttributes

TSession

TDatabase

TTable

TQuery

TStoredProc

TWebDispatcher

TCustomDispatcher

TDSTableProducer


Working with sockets

Section topics 	See also

These topics describes the socket components that let you create an application that can communicate with other systems using TCP/IP and related protocols. Using sockets, you can read and write over connections to other machines without worrying about the details of the actual networking software. Sockets provide connections based on the TCP/IP protocol, but are sufficiently general to work with related protocols such as Xerox Network System (XNS), Digital's DECnet, or Novell's IPX/SPX family.

Delphi Client/Server lets you write network servers or client applications that read from and write to other systems. A server or client application is usually dedicated to a single service such as Hypertext Transfer Protocol (HTTP) or File Transfer Protocol (FTP). Using server sockets, an application that provides one of these services can link to client applications that want to use that service. Client sockets allow an application that uses one of these services to link to server applications that provide the service.


Implementing services

Section topics 	See also

Sockets provide one of the pieces you need to write network servers or client applications. For many services, such as HTTP or FTP, third party servers are readily available. Some are even bundled with the operating system, so that there is no need to write one yourself. However, when you want more control over the way the service is implemented, a tighter integration between your application and the network communication, or when no server is available for the particular service you need, then you may want to create your own server or client application. For example, when working with distributed data sets, you may want to write a layer to communicate with the databases on other systems.


Understanding service protocols

Section topics 	See also

Before you can write a network server or client, you must understand the service that your application is providing or using. Many services have standard protocols that your network application must support. If you are writing a network application for a standard service such as HTTP, FTP, or even finger or time, you must first understand the protocols used to communicate with other systems. See the documentation on the particular service you are providing or using.

If you are providing a new service for an application that communicates with other systems, the first step is designing the communication protocol for the servers and clients of this service. What messages are sent? How are these messages coordinated? How is the information encoded?

Communicating with applications

Often, your network server or client application provides a layer between the networking software and an application that uses the service. For example, an HTTP server sits between the Internet and a Web server application that provides content and responds to HTTP request messages.

Sockets provide the interface between your network server or client application and the networking software. You must provide the interface between your application and the applications that use it. You can copy the API of a standard third party server (such as ISAPI), or you can design and publish your own API.


Services and ports

Section topics 	See also

Most standard services are associated, by convention, with specific port numbers. We will discuss port numbers in greater detail later. For now, consider the port number a numeric code for the service.

If you are implementing a standard service, Delphi's Windows socket objects provide methods for you to look up the port number for the service. If you are providing a new service, you can specify the associated port number in a SERVICES file on Windows 95 or NT machines. See the Microsoft documentation for Windows sockets for more information on setting up a SERVICES file.


Describing socket connections

Section topics 	See also

Sockets let your network application communicate with other systems over the network. Each socket can be viewed as an endpoint in a network connection. It has an address that specifies

The system it is running on.

The types of interfaces it understands.

The port it is using for the connection.

To fully describe a socket connection, you must provide the addresses of the sockets on both ends of the connection.


Types of connections

Section topics 	See also

Socket connections can be divided into three basic types, which reflect how the connection was initiated, and what the local socket is connected to. These are client connections, listening connections, and server connections.

Client connections

Client connections connect a client socket on the local system to a server socket on a remote system. Client connections are initiated by the client socket. First, the client socket must describe the server socket it wishes to connect to. The client socket then looks up the server socket and, when it locates the server, requests a connection. The server socket may not complete the connection right away. Server sockets maintain a queue of client requests, and complete connections as they find time. When the server socket accepts the client connection, it sends the client socket a full description of the server socket to which it is connecting, and the connection is completed by the client.

Listening connections

Server sockets do not locate clients. Instead, they form passive, "half connections" that listen for client requests. Server sockets associate a queue with their listening connections; the queue records client connection requests as they come in. When the server socket accepts a client connection request, it forms a new socket to connect to the client, so that the listening connection can remain open to accept other client requests.

Server connections

Server connections are formed by server sockets when the listening socket accepts a client request. A description of the server socket that completes the connection to the client is sent to the client when the server accepts the connection. The connection is established when the client socket receives this description and completes the connection.

Once the connection to a client socket is completed, the server connection is indistinguishable from a client connection. Both endpoints have the same capabilities and receive the same types of events. Only the listening connection is fundamentally different, as it has only a single endpoint.


Describing the socket

Section topics 	See also

Before you can make a socket connection, you must describe the sockets that form its endpoints. Some of the information is available from the system your application is running on. For instance, you do not need to describe the local IP address of a client socket -- this information is available from the operating system.

The information you must provide depends on the type of socket you are working with. Client sockets must describe the server they want to connect to. Listening server sockets must describe the port that represents the service they provide.

A full description of the endpoint of a socket connection includes both the IP address and the port number.


Describing the host

Section topics 	See also

The host is the system that is running the application that contains the socket. You can describe the host for a socket by giving its IP address, which is a string of four numeric (byte) values in the standard Internet dot notation, such as

123.197.1.2

A single system may support more than one IP address.

IP addresses are often difficult to remember and easy to mistype. An alternative is to use the host name. Host names are aliases for the IP address that you often see in Uniform Resource Locators (URLs). They are strings containing a domain name and service, such as

http://www.wSite.Com

Most Intranets provide host names for the IP addresses of systems on the Internet. On Windows 95 and NT machines, if a host name is not available, you can create one for your local IP address by entering the name into the HOSTS file. See the Microsoft documentation on Windows sockets for more information on the HOSTS file.

Server sockets do not need to specify a host. The local IP address can be read from the system. If the local system supports more than one IP address, server sockets will listen for client requests on all IP addresses simultaneously. When a server socket accepts a connection, the client socket provides the remote IP address.

Client sockets must specify the remote host by providing either its host name or IP address.

Choosing between a host name and an IP address

Most applications use the host name to specify a system. Host names are easier to remember, and easier to check for typographical errors. Further, servers can change the system or IP address that is associated with a particular host name. Using a host name allows the client socket to find the abstract site represented by the host name, even when it has moved to a new IP address.

If the host name is unknown, the client socket must specify the server system using its IP address. Specifying the server system by giving the IP address is faster. When you provide the host name, the socket must search for the IP address associated with the host name, before it can locate the server system.


Using ports

Section topics 	See also

While the IP address provides enough information to find the system on the other end of a socket connection, you also need a port number on that system. Without port numbers, a system could form only a single connection at a time. Port numbers are unique identifiers that enable a single system to host multiple connections simultaneously, by giving each connection a separate port number.

Earlier, we described port numbers as a numeric codes for the services implemented by network applications. This is actually just a convention that allows listening server connections to make themselves available on a fixed port number so that they can be found by client sockets. Server sockets listen on the port number associated with the service they provide. When they accept a connection to a client socket, they create a separate socket connection that uses a different, arbitrary, port number. This way, the listening connection can continue to listen on the port number associated with the service.

Client sockets use an arbitrary local port number, as there is no need for them to be found by other sockets. They specify the port number of the server socket to which they want to connect so that they can find the server application. Often, this port number is specified indirectly, by naming the desired service.


Using socket components

Section topics 	See also

Delphi provides two socket components that allow your network application to form connections to other machines, and that allow you to read and write information over that connection. Associated with each of these socket components are Windows socket objects, which represent the endpoint of an actual socket connection. The socket components use the Windows socket objects to encapsulate the Windows socket API calls, so that your Delphi application does not need to be concerned with the details of establishing the connection or managing the socket messages.

If you want to work with the Windows socket API calls, or customize the details of the connections that Delphi makes on your behalf, you can use the properties, events, and methods of the Windows socket objects.


Using client sockets

Section topics 	See also

Add a client socket component (TClientSocket) to your form or data module to turn your application into a TCP/IP client. Client sockets allow you to specify the server socket you want to connect to, and the service you want that server to provide. Once you have described the desired connection, you can use the client socket component to complete the connection to the server.

Each client socket component uses a single client Windows socket object (TClientWinSocket) to represent the client endpoint in a connection.

Specifying the desired server

Client socket components have a number of properties that allow you to specify the server system and port to which you want to connect. You can specify the server system by its host name using the Host property. If you do not know the host name, or if you are concerned about the speed of locating the server, you can specify the IP address of the server system by using the Address property. You must specify either a host name or an IP address. If you specify both, the client socket component will use the host name.

In addition to the server system, you must specify the port on the server system that your client socket will connect to. You can specify the server port number directly using the Port property, or indirectly by naming the desired service using the Service property. If you specify both the port number and the service, the client socket component will use the service name.

Forming the connection

Once you have set the properties of your client socket component to describe the server you want to connect to, you can form the connection at runtime by calling the Open method. If you want your application to form the connection automatically when it starts up, set the Active property to True at design time, using the Object Inspector.

Getting information about the connection

After completing the connection to a server socket, you can use the client Windows socket object associated with your client socket component to obtain information about the connection. Use the Socket property to get access to the client Windows socket object. This Windows socket object has properties that enable you to determine the address and port number used by the client and server sockets to form the endpoints of the connection. You can use the SocketHandle property to obtain a handle to the socket connection to use when making Windows socket API calls. You can use the Handle property to access the window that receives messages from the socket connection. The ASyncStyles property determines what types of messages that window handle receives.

Closing the connection

When you have finished communicating with a server application over the socket connection, you can shut down the connection by calling the Close method. The connection may also be closed from the server end. If that is the case, you will receive notification in an OnDisconnect event.


Using server sockets

Section topics 	See also

Add a server socket component (TServerSocket) to your form or data module to turn your application into a TCP/IP server. Server sockets allow you to specify the service you are providing or the port you want to use to listen for client requests. You can use the server socket component to listen for and accept client connection requests.

Each server socket component uses a single server Windows socket object (TServerWinSocket) to represent the server endpoint in a listening connection. It also uses a server client Windows socket object (TServerClientWinSocket) for the server endpoint of each active connection to a client socket that the server accepts.

Specifying the port

Before your server socket can listen to client requests, you must specify the port that your server will listen on. You can specify this port using the Port property. If your server application is providing a standard service that is associated by convention with a specific port number, you can specify the port number indirectly using the Service property. It is a good idea to use the Service property, as it is easy to miss typographical errors made when setting the port number. If you specify both the Port property and the Service property, the server socket will use the service name.

Listening for client requests

Once you have set the port number of your server socket component, you can form a listening connection at runtime by calling the Open method. If you want your application to form the listening connection automatically when it starts up, set the Active property to True at design time, using the Object Inspector.

Forming connections to clients

A listening server socket component automatically accepts client connection requests when they are received. You receive notification every time this occurs in an OnClientConnect event.

Getting information about connections

Once you have opened a listening connection with your server socket, you can use the server Windows socket object associated with your server socket component to obtain information about the connection. Use the Socket property to get access to the server Windows socket object. This Windows socket object has properties that enable you to find out about all the active connections to client sockets that were accepted by your server socket component. Use the SocketHandle property to obtain a handle to the socket connection to use when making Windows socket API calls. Use the Handle property to access the window that receives messages from the socket connection.

Each active connection to a client application is encapsulated by a server client Windows socket object (TServerClientWinSocket). You can access all of these through the Connections property of the server Windows socket object. These server client Windows socket objects have properties that enable you to determine the address and port number used by the client and server sockets which form the endpoints of the connection. You can use the SocketHandle property to obtain a handle to the socket connection to use when making Windows socket API calls. You can use the Handle property to access the window that receives messages from the socket connection. The ASyncStyles property determines what types of messages that window handle receives.

Closing the connection

When you want to shut down the listening connection, call the Close method. This shuts down all open connections to client applications, cancels any pending connections that have not been accepted, and then shuts down the listening connection so that your server socket component does not accept any new connections.

When clients shut down their individual connections to your server socket, you are informed by an OnClientDisconnect event.


Reading and writing over socket connections

Section topics 	See also

The reason you form socket connections to other machines is so that you can read or write information over those connections. What information you read or write, or when you read it or write it, depends on the service associated with the socket connection.

Reading and writing over sockets can occur asynchronously, so that it does not block the execution of other code in your network application. This is called a non-blocking connection. You can also form blocking connections, where your application waits for the reading or writing to be completed before executing the next line of code.


Non-Blocking connections

Section topics 	See also

For client sockets, set the ClientType property to ctNonBlocking to form a non-blocking connection. When the connection is non-blocking, your socket is informed when the server on the other end of the connection tries to read or write information over the connection. You can respond to these notifications in an OnRead or OnWrite event handler.

For server sockets, set the ServerType property to stNonBlocking to form a non-blocking connection. As with a non-blocking client connection, a non-blocking server receives notification when the client on the other end of the connection tries to read or write information over the connection. You can respond to these notifications in an OnClientRead or OnClientWrite event handler.

Reading and writing events

The Windows socket object associated with the socket connection is provided as a parameter to the read or write event handlers. This Windows socket object provides a number of methods to allow you to read or write over the connection.

To read from the socket connection, use the ReceiveBuf or ReceiveText method. Before using the ReceiveBuf method, use the ReceiveLength method to determine the number of bytes the socket on the other end of the connection is ready to send.

To write to the socket connection, use the SendBuf, SendStream, or SendText method. If you have no more need of the socket connection after you have written your information over the socket, you can also the SendStreamThenDrop method. SendStreamThenDrop closes the socket connection after writing all information that can be read from the stream. If you use the SendStream or SendStreamThenDrop method, do not free the stream object. The socket frees the stream automatically when the connection is closed.

Note:	SendStreamThenDrop will close down a server connection to an individual client, not a listening connection.


Blocking connections

Section topics 	See also

When the connection is blocking, your socket must initiate reading or writing over the connection, rather than waiting passively for a notification from the socket connection. Use a blocking socket when your end of the connection is in charge of when reading and writing takes place.

For client sockets, set the ClientType property to ctBlocking to form a blocking connection. Depending on what else your client application does, you may want to create a new execution thread for reading or writing, so that your application can continue executing code on other threads while it waits for the reading or writing over the connection to be completed.

For server sockets, set the ServerType property to stThreadBlocking to form a blocking connection. Because blocking connections hold up the execution of all other code while the socket waits for information to be written or read over the connection, server socket components always spawn a new execution thread for every client connection when the ServerType is stThreadBlocking.

Using threads with blocking connections

Client sockets do not automatically spawn new threads when reading or writing using a blocking connection. If your client application has nothing else to do until the information has been read or written, this is what you want. If your application includes a user interface that must still respond to the user, however, you may want to generate a separate thread for the reading or writing.

When server sockets form blocking connections, they always spawn separate threads for every client connection, so that no client must wait until another client has finished reading or writing over the connection. Server sockets use TServerClientThread objects to implement the execution thread for each connection. When the server client thread begins executing, it checks whether the client on the other end of the connections is trying to write over the connection, and if so, it generates an OnClientRead event. If the client is not trying to write over the connection, it generates an OnClientWrite event so that the server will start writing.

Using TWinSocketStream

Non-blocking connections, and blocking server connections all receive events that indicate when the connection is ready for reading or writing. The event handler can then use the methods of the Windows socket object to do the actual reading or writing.

Blocking client connections, however, must find out for themselves when the socket on the other end of the connection is ready for reading or writing. If you use a TWinSocketStream object to read from or write to the connection, it provides methods to help coordinate the timing of reading and writing. Call the WaitForData method to wait until the server socket is ready.

When reading or writing using TWinSocketStream, the stream times out if the reading or writing has not completed after a specified period of time. As a result of this timing out, the socket application will never end up hanging endlessly because of a dropped connection. In order to take advantage of this timing out, you may want to use TWinSocketStream for reading and writing over blocking server sockets, even though they receive reading and writing events.

Note:	You can not use TWinSocketStream with a non-blocking connection.


Responding to socket events

Section topics 	See also

You have already learned about some of the events received by socket components. We have seen the events that occur in non-blocking sockets and blocking server sockets that tell the socket when to begin reading or writing over the socket connection. We have also seen the OnDisconnect event received by client sockets when the server ends a connection, or the OnClientDisconnect event received by server sockets when the client ends a connection.

In addition, both client sockets and server sockets generate an OnError event when they receive error messages from the connection. Socket components also receive a number of events in the course of opening and completing a connection. If your application needs to influence how the opening of the socket proceeds, or if it should start reading or writing once the connection is formed, you will want to write event handlers to respond to these events.


Error events

Section topics 	See also

Both client sockets and server sockets generate an OnError event when they receive error messages from the connection. You can write an OnError event handler to respond to these error messages. The OnError event handler is passed information about what the socket was trying to do when the error occurred, and the error code that was provided by the error message. You can respond to the error in the OnError event handler, and change the error code to 0 to prevent the socket from raising an exception.


Client events

Section topics 	See also

When a client socket opens a connection, the following events occur:

1.	An OnLookup event occurs prior to an attempt to locate the server socket. At this point you can not change the Host, Address, Port, or Service properties to change the server socket that is located. You can use the Socket property to access the client Windows socket object, and use its SocketHandle property to make Windows API calls that affect the client properties of the socket. For example, if you want to set the port number on the client application, you would do that now before the server client is contacted.

2.	The Windows socket is set up and initialized for event notification.

3.	An OnConnecting event occurs after the server socket is located. At this point, the Windows Socket object available through the Socket property can provide information about the server socket that will form the other end of the connection. This is the first chance to obtain the actual port and IP address used for the connection, which may differ from the port and IP address of the listening socket that accepted the connection.

4.	The connection request is accepted by the server and completed by the client socket.

5.	An OnConnect event occurs after the connection is established. If your socket should immediately start reading or writing over the connection, write an OnConnect event handler to do it.


Server events

Section topics 	See also

Server socket components form two types of connections: listening connections and connections to client applications. The server socket receives events during the formation of each of these connections.

Events when listening

Just before the listening connection is formed, the OnListen event occurs. At this point you can obtain the server Windows socket object through the Socket property. You can use its SocketHandle property to make changes to the socket before it is opened for listing. For example, if you want to restrict the IP addresses the server uses for listening, you would do that in an OnListen event handler.

Events with client connections

When a server socket accepts a client connection request, the following events occur:

1.	The server socket generates an OnGetSocket event, passing in the Windows socket handle for the socket that forms the server endpoint of the connection. If you want to provide your own customized descendant of TServerClientWinSocket, you can create one in an OnGetSocket event handler, and that will be used instead of TServerClientWinSocket.

2.	An OnAccept event occurs, passing in the new TServerClientWinSocket object to the event handler. This is the first point when you can use the properties of TServerClientWinSocket to obtain information about the server endpoint of the connection to a client.

3.	If ServerType is stThreadBlocking an OnGetThread event occurs. If you want to provide your own customized descendant of TServerClientThread, you can create one in an OnGetThread event handler, and that will be used instead of TServerClientThread.

4.	If ServerType is stThreadBlocking, an OnThreadStart event occurs as the thread begins execution. If you want to perform any initialization of the thread, or make any Windows socket API calls before the thread starts reading or writing over the connection, use the OnThreadStart event handler.

5.	The client completes the connection and an OnClientConnect event occurs. With a non-blocking server, you may want to start reading or writing over the socket connection at this point.


See also

TClientSocket

TClientWinSocket

TServerSocket

TServerWinSocket

TServerClientWinSocket

TServerClientThread

TWinSocketStream

Internet components


OLE and ActiveX Overview
«OLE and ActiveX Overview

Section topics 	See also

This overview begins with a description of COM, the Component Object Model, which is the foundation for OLE and ActiveX technologies. These topics describe COM and its benefits, and provides a background for using COM-based technologies in Delphi. Later topics discuss in greater detail how OLE and ActiveX are surfaced in Delphi, focusing on OLE Automation and ActiveX object creation.


An Overview of COM

Section topics 	See also

COM is the Component Object Model, an object-based programming specification designed to provide robust object interoperability through sets of predefined routines called interfaces. COM is based on a binary standard, rather than a source code standard, thereby enabling objects written in different languages, running in different process spaces and on different platforms to communicate. COM objects can also be transparently extended, modified and updated because unique identifiers are used to create them and to access their interfaces. COM also has a library containing a set of standard interfaces that define the core functionality of a COM object, and a small set of API functions designed for the purpose of creating and managing COM objects.

As an extensible systems software architecture, COM is the basis for other technologies such as OLE and ActiveX. These technologies are operating system extensions that define their own rules and provide their own libraries for creating and manipulating objects of those types. Using COM as a foundation, developers can create their own extensions so that objects created according to their rules can interact with other COM-based technologies.

Delphi has classes and wizards that make it easy to create the essential elements of a COM, OLE or ActiveX application. The Delphi object framework includes classes that range from simple, COM-compatible classes for use within a single application, to those that can be used for OLE Automation servers and ActiveX controls. Using Delphi to create COM-based applications offers a wide range of possibilities, from improving software design by using interfaces internally in an application, to creating objects that can interact with other COM-based API objects on the system, such as the Win95 Shell extensions and DirectX multimedia support.

The first of these topics summarizes each the following fundamental pieces of a COM or ActiveX application, indicating where and when Delphi classes and tools are available for working with them.

Interface: A set of method prototypes, with a clearly defined purpose

COM object: An instance of a class, called a CoClass, that implements the methods of COM interfaces

COM/ActiveX server: A module, either an EXE, DLL, or OCX that contains the code for COM or ActiveX objects

Class factory: An object that can create a COM object from a specified CoClass

Type library: A binary symbol file, often saved as a resource, that contains the type information for a COM or ActiveX server


Interfaces

Section topics 	See also

Interfaces are the building blocks of COM. They are groups of semantically-related routines through which COM objects communicate. Interfaces are defined by the following rules.

Interface methods are abstract

An interface is a set of method prototypes whose functionality the interface defines. These method definitions include the number and types of their parameters, their return type, and their expected behavior. How those methods are implemented is not defined, so with regard to the interface, the actual method implementation is always completely hidden. Therefore, as long as the class's implementation conforms to the interface definition, the interface is fully polymorphic, meaning that accessing and using the interface is the same for any implementation of it. In this regard, an interface is similar to a class with only abstract methods and a clear intention of how they are to be used. Like abstract classes, interfaces themselves can never be instantiated. Classes that implement interfaces must be instantiated in order to use them.

An interface conforms to a binary standard

Because all interface methods are abstract, an interface can be defined as a pointer to a vtable. Each entry in the vtable references the implementation of the corresponding interface method in the class that implements the interface. Defining an interface to be a pointer establishes a protocol for accessing COM objects that is binary-compliant. Consequently, obtaining access to an interface implementation on an object is handled simply by providing a pointer to an interface vtable.

Note:	The term vtable is used throughout these topics for historical reference. Most information about COM interfaces refers to this term. For clarification, the vtable is to an interface as the VMT (virtual method table) is to a Delphi class.

An interface encapsulates specific functionality

Interface methods are semantically-related to provide the functionality for a specified purpose. Accordingly, interfaces are usually named for their behavior and are prefaced with a capital I. For example, the IMalloc interface allocates, frees, and manages memory. Similarly, the IPersist interface is the base interface for three other standard persistence-related interfaces that provide method prototypes for loading and saving the state of an object to a storage, stream, or file. A class that implements the interface routines can therefore provide its service to clients. Clients are the objects and applications that use the class.

An interface has a unique identifier

Interfaces are strongly typed using a globally unique identifier (GUID) to ensure that they do not conflict with one another. A GUID is a specific type of a universally unique identifier (UUID). It is a a 16-byte (128-bit) binary value that is guaranteed to be unique. GUIDs that are used to identify interfaces are referred to as interface identifiers (IIDs), and each interface must have an IID associated with it. Identifying interfaces by a unique value, rather than by name, eliminates versioning problems that can occur when existing code is updated and names can conflict with either old versions of the same libraries and routines, or between products from different vendors.

Note:	The Delphi wizards simplify the process of assigning GUIDs by automatically generating them for you upon request.

An interface contract is immutable

An interface is a an immutable contract that guarantees a particular functionality. The interface contract specifies its methods, their behavior, and what their input and output must be. Consequently, interface definitions must never change, once they are published. Any change to the semantics or methods of an interface will necessarily define of a new interface. Objects can, however, safely support multiple versions of an interface because each version is accessed by its IID, rather than by name.

Interfaces inherit basic functionality from their ultimate ancestor

All interfaces derive either directly or indirectly from the IUnknown interface. This interface provides the essential functionality of an interface, that is, dynamic querying and lifetime management. This functionality is established in the three IUnknown methods which are, in vtable order, QueryInterface, AddRef and Release. Every class that implements interfaces must implement the three IUnknown methods, as well as all of the methods declared by any other ancestor interfaces, and all of the methods declared by the interface itself.

QueryInterface provides a way for clients to query a given object and access pointers to the interfaces it supports. As the first entry in the vtable, QueryInterface is a memory-efficient way of determining the capabilities of an object. QueryInterface makes it possible to update a COM object without the overhead of updating other dependent objects because the object can be dynamically queried. For more information about this aspect of QueryInterface, see "Using classes that support interfaces."

The second and third methods in the vtable for every interface are AddRef and Release. These are simple reference counting methods that enable an object to track its own lifetime and to delete itself when appropriate. While the reference count is nonzero the object must remain in memory. Once the reference count reaches zero the interface implementation can safely dispose of the underlying object(s). These methods provide the life-time management of COM objects.

Note:	Delphi provides objects which manage most of the IUnknown reference counting and interface querying for you, so you do not need to implement this functionality yourself. For more information about reference counting with interfaces, see Object Pascal Language Guide.

"Using interfaces in Delphi" describes the categories of interfaces associated with different COM technologies. Where relevant, the corresponding Delphi CoClasses that implement interfaces for those technologies are mentioned.


COM objects

Section topics 	See also

A COM object is an object that is instantiated from a CoClass, which is a class that implements one or more interfaces. The COM object provides the services indicated by each interface its CoClass supports. Any time a COM object is used it is referenced by a pointer to one of its interfaces. This establishes two important features of a COM object, which are:

With access only through function pointers, no external manipulation of a COM object can directly modify its data.

Because an interface reference is a pointer, COM objects can be used by any language, with any internal state representation, as long as that language can create pointers to structures, or arrays, of function pointers.

The following diagram shows the vtable layout for a hypothetical interface, called MyInterface, implemented in a COM object:

Interface vtable

A CoClass must have a class factory and a class identifier ( CLSID) so that its COM object can be externally instantiated (from another module). For more information about class factories, see "Class factories." Using these unique identifiers for CoClasses means that they can be transparently updated whenever new interfaces are implemented in their class. Because interfaces are also accessed by unique identifiers rather than by names, CoClasses can support both old and new versions of an interface (as a collection of methods and implementations). The new interface can modify or add methods without a conflict of versions, which is a common problem when using DLLs. Moreover, interface pointers are polymorphic, allowing any kind of interface pointer to manipulate any kind of COM object. With COM, even objects built by different vendors at different times can interact without conflict.


COM and ActiveX servers

Section topics 	See also

A COM server is an application or a library that provides services to a client application or library. A COM server can be an in-process server, meaning a DLL running in the same process space as the client, a local server, meaning an EXE running in a different process space but on the same machine as the client, or a remote server, meaning an application running on a different machine from that of the client. COM servers are the modules in which COM objects exist. A COM server that contains the code for OLE Automation objects, ActiveX controls and ActiveForms is an ActiveX server. Delphi has classes that encapsulate the basic functionality of an ActiveX server, which includes:

Exporting the necessary routines that handle server registration, class registration, loading and unloading the server, and object instantiation

Creating and managing the class factories for objects implemented on the server

Providing essential information about the server, including its type, Help file, server name, type library and so on

The Delphi server classes require a type library. Information about the objects and interfaces available on the server is found in that module's type library. For an overview of type libraries, see "Type libraries." For more information on creating type libraries, see "Working with type libraries." For information about writing an OLE Automation server, see "Creating OLE Automation servers."


Class factories

Section topics 	See also

Class factories are special types of objects used to create and register COM objects. Class factories provide a standard mechanism for instantiating CoClasses. Classes without a CLSID and a class factory must be instantiated by calling a constructor. Using class factories to instantiate objects eliminates the need for a client to know anything about a CoClass, other than its unique identifier. The class factory handles all of the specifics of constructor methods and arguments and so on.

Typically for each CoClass definition there is a class factory. A class factory class can be associated with more than one CoClasses, each of which it can instantiate. However, when a class factory is itself instantiated, it is associated with a particular class identifier (CLSID) that is passed to its constructor. This parameter indicates which CoClass that particular class factory object can instantiate. Therefore, while class factory classes can be used for more than one CoClass, each instance of a class factory can be used to create instances of only one CoClass.

Instantiating CoClasses

CoClasses can be instantiated by calling the global Windows API function CoGetClassObject, which looks up the CLSID in the registry, finds the path for the server, loads the server, and provides a pointer to a class factory interface (usually IClassFactory). The IClassFactory pointer can be used to call the class factory's CreateInstance method, which creates the instance of the CoClass.

Alternatively you can call the global Windows API function CoCreateInstance, which will do all of the above steps. After loading the server, CoCreateInstance automatically calls the CoGetClassObject function and, through the IClassFactory pointer, the CreateInstance method. However, when creating multiple instances of a class, it is faster to create only one class factory for that CLSID and then call the factory's CreateInstance method directly. Only CoGetClassObject can provide an IClassFactory interface pointer so that you can directly use that particular instance of the class factory more than once. Therefore, if you intend to create more than one instance of a specific CoClass in a performance critical situation, it is faster to call CoGetClassObject and save the overhead of checking the registry and loading the server for each instantiation.

Delphi class factory classes

For an object to be a class factory it must support the IClassFactory interface. The Delphi CoClasses have corresponding class factory classes. These classes implement either the IClassFactory interface, or, for ActiveX objects that need licensing, the IClassFactory2 interface. Delphi class factory objects are created in the initialization section of the unit that includes the server, so that the class factories are automatically available when the server is loaded. The Delphi COM server class used for COM or ActiveX servers, has methods that manage the class factories. Class factories are reference counted, so that the server will know when it can be unloaded.


Type libraries

Section topics 	See also

Type libraries provide a way for you to identify what types of objects and interfaces are available on an ActiveX server. A type library is a file that contains type information for a COM or ActiveX server similar to the way in which a unit, a header file, a DEF file, or an import library contains the necessary information about an application, a DLL, or a Windows EXE. In fact, much of the symbol information stored in these files can be stored in a type library. You can obtain this type of information either by querying a running instance of an object or by loading and reading type libraries.

This section describes what a type library contains, how it is created, when it is used, and how it is accessed. For developers wanting to share interfaces across languages, the section ends with suggestions on using type library tools.

The contents of type libraries

Type libraries contain typeinfo, which indicates which interfaces exist in which COM objects, and the types and numbers of arguments to the interface methods. These descriptions include the unique identifiers for the CoClasses (CLSIDs) and the interfaces (IIDs), so that they can be properly accessed, as well as the dispatch identifiers (dispIDs) for OLE Automation interface methods. Type libraries can also contain:

Descriptions of custom type information associated with custom interfaces

Routines that are exported by the ActiveX server, but that are not interface methods

Information about enumeration data types

References to type descriptions from other type libraries.

For more information about using type libraries with dispatch interfaces, see "OLE Automation interfaces."

Creating type libraries

With traditional development tools, you create type libraries by writing scripts in the Interface Description Language (IDL) or the Object Description Language (ODL), then running that script through a compiler. However, Delphi provides a Type Library editor to help you create a type library without having to learn and use IDL or ODL. The Type Library editor automatically generates a standard type library (by default, as a resource), as well as a Delphi Interface File (.PAS file) that contains the interface definition in Object Pascal syntax. For more information on using the Type Library editor to write interfaces and CoClasses, see "Working with type libraries."

When to use type libraries

It is important to create a type library for each set of objects that are exposed to external users. Situations in which a type library is either useful or required are listed below.

Exposed objects that support vtable binding must be described in a type library because vtable references are bound at compile time. For more information about vtable and compile time binding, see "OLE Automation interfaces."

Objects instantiated from classes that support the IProvideClassInfo interface, such as all descendants of the Delphi TTypedComObject class, must have a type library.

ActiveX controls require a type library, which must be included as a resource in the DLL or OCX that contains the ActiveX controls.

Applications that implement OLE Automation servers should provide a type library so that clients can early bind to it.

Type libraries are not required, but are useful for identifying the objects used with drag-and-drop.

The requirements for type libraries for different objects are indicated in the table "OLE/ActiveX object requirements:." For more information about compile time or early-binding, which requires a type library, see "OLE Automation interfaces."

When defining interfaces for internal use only (within an application) you do not need to create a type library. You can write the interface definition directly in an Object Pascal unit. The syntax for declaring interfaces in Delphi is described in Object Pascal Language Guide.

Accessing type libraries

The binary type library is normally a part of a resource file (.RES) or a stand-alone file with a .TLB file-name extension. Once a type library has been created Object browsers, compilers, and similar tools can access type libraries through special type interfaces: ITypeLib, ITypeInfo, and ITypeComp. The ITypeLib interface provides access to information about the type information in a type library and the descriptions of individual objects are accessed through the ITypeInfo interface. Delphi can import and use type libraries from other applications. Most of the Delphi classes used for OLE and ActiveX applications support the essential interfaces that are used to store and retrieve type information from type libraries and from running instances of an object. The Delphi class TTypedComObject supports interfaces that provide type information, and is used as a foundation for the Delphi ActiveX object framework.

Benefits of using type libraries

Even if your application does not require a type library, you may want to consider the following benefits of using one:

Type checking can be performed at compile time.

You can use early binding with OLE Automation (as opposed to calling through Variants), and controllers that do not support vtables or dual interfaces can encode dispIDs at compile time, improving run-time performance.

Type browsers can scan the library, so clients can see the characteristics of your objects.

The RegisterTypeLib function can be used to register your exposed objects in the registration database.

The UnRegisterTypeLib function can be used to completely uninstall an application's type library from the system registry.

Local server access is improved because OLE Automation uses information from the type library to package the parameters that are passed to an object in another process.

Using type library tools for sharing interfaces with C++

For tasks that require header files for sharing interfaces between languages you can use a combination of the Delphi Type Library editor and standard type library command-line tools to generate the code. The tools for working with type libraries are listed below.

The Microsoft IDL compiler (MIDL) compiles IDL scripts to create a type library. MIDL has an optional switch for generating header files.

MKTYPLIB is an ODL compiler that compiles ODL scripts to create a type library.

The Delphi TLIBIMP (Type Library Import) tool, which takes existing type libraries and creates Delphi Interface files, is incorporated into the Delphi Type Library editor.

OLEView is a type library browser tool, found on Microsoft's web site.

To share an interface between Object Pascal and C or C++ you will want to have a header file. You could write an IDL file for C or C++, and compile it with both the header and type library switches on. However, if you are writing an application in Object Pascal using Delphi, you can more easily generate a header file that contains the interface by following these steps:

1.	Use the Delphi Type Library editor to write a type library file in Object Pascal.

2.	Then take the type library and open it in OLEView.

3.	Copy and paste the IDL script that appears on the right side into a .IDL file.

4.	Then compile it with MIDL to get the C header file.

The header file will contain information about your interface, which can be used by C and C++ applications.


Using interfaces in Delphi

Section topics 	See also

When and how to use interfaces is determined by the type of service you want to provide, and the process spaces in which you need to run your application. Predefined interfaces that provide services for specific types of objects, and the rules for using them, are available in extensions to the COM library. The following sections discuss how to select, create, and implement interfaces, and the benefits of using them in a Delphi application.


Using interfaces as services

Section topics 	See also

An interface provides a way for an application to access an object's services such as saving state, visual editing, or drag-and-drop. The type of object you create, and the interfaces it must or can implement, depend upon the service you are using, and whether you are creating a client or a server. Here are some guidelines on how to proceed.

Predefined services

For predefined services you create either client or server applications; that is, you either access and use the service, or you provide the service to clients. Applications can be clients and servers. If you are writing a client application for a particular, predefined service, you need to know what type of objects provide that service and how to access them. You need to understand the mechanism for creating instances of the objects that use the service, and then implement the appropriate interfaces. If you are a server, you must understand the mechanism for creating objects that provide the service, and implement the appropriate interfaces to provide that service.

Services that are standard for all applications of a specific type, such as COM, OLE, or ActiveX, are defined in interfaces provided by those libraries. For example, for OLE applications the services provided by interfaces fall into one of four general areas:

Communication between objects

Infrastructure support

Basic linking and embedding

Advanced features

To create an object that has basic OLE functionality you would need to implement only a few interfaces from those in the first two categories. For OLE Documents or ActiveX controls, you need to implement interfaces from the other categories as well. The specific rules for each type of object are described in the corresponding OLE or ActiveX libraries.

Custom services

When creating custom services you need to define the basics of your particular service's software architecture, so that objects can be created and implemented within that context. As with predefined services, clients and servers must adhere to the rules defined for creating, accessing and manipulating objects of your type.


Using interface libraries

Section topics 	See also

When you create and use objects of a particular type, refer to the corresponding library and rules for standard objects of that type. The following is a summary of the types of interface libraries and extensions available.

COM interfaces

COM defines a standard library of interfaces. These include the interface IUnknown that establishes the basic functionality of an interface, and the IClassFactory interface that is required by class factory objects for externally instantiating a COM objects. Developers interested in creating classes that implement COM interfaces should refer to the TInterfacedObject, TComObject, TTypedComObject and the TComObjectFactory and TTypedComObjectFactory classes that the Delphi COM framework provides.

OLE/ActiveX extensions

The OLE library extension to COM encompasses a number of object types, each with its own definition of the interfaces it can or must support. OLE itself provides a number of general-purpose interfaces typically begin with "IOle".

Like OLE, ActiveX is an implementation of COM. Common applications of ActiveX technology are Active Documents and ActiveX controls. ActiveX is more recent than OLE, and was developed as an extension to COM optimized for size and speed. However, as OLE has evolved and ActiveX has emerged, these two technologies have begun to overlap and their distinctions are beginning to break down.

The following diagram illustrates the relationship of the OLE/ActiveX extensions and how they are built upon COM:

Diagram of OLE/ActiveX technologies

OLE Documents

OLE Documents is a library of interfaces for document objects and containers. The library provides a variety of services through its own interfaces, such as the communication between the container and object applications for linking and embedding. Developers interested in creating OLE document containers can use the TOleContainer class.

OLE Automation

OLE Automation requires the implementation of the IDispatch interface and support for the dispatch interface type. OLE Automation includes services that are provided using interfaces for the packaging and sending of parameters between the different process spaces, providing storage for objects, and translation of object names. Developers interested in OLE Automation can use TAutoObject which implements the IDispatch interface.

ActiveX controls

ActiveX controls, also known as OLE controls or OCXs, are objects that implement interfaces in the ActiveX library. These interfaces provide the ability of an object to fire events, bind to data sources, and support licensing. Delphi provides the TActiveXControl and TActiveForm classes, as well as wizards to help you easily create ActiveX objects.

Implementing custom/third party interfaces

Any developer can create custom interfaces when deploying object-based applications. For services where third-party standards groups have defined standard extensions, you can use their standards, as you would those of the COM, OLE, and ActiveX libraries. You can also define your own libraries and rules. When creating objects that implement custom or third-party interfaces, TComObject, TTypedComObject, or TAutoObject are suggested for use as a base class.


Creating and accessing interfaces

Section topics 	See also

You can use predefined interfaces that are available through COM, OLE, or third-party libraries, or you can create your own interfaces. To create your own interface, write an interface definition, and then create a class that implements the interface. The syntax for declaring classes that support interfaces in Delphi is described in Object Pascal Language Guide. It also discusses the details of the steps for creating and using interfaces in Delphi, which are listed below:

Declaring an interface.

Creating a class definition that support interfaces.

Implementing interface methods in the class.

Creating an interface reference.

Invoking interface methods through an interface reference.

Querying for interfaces on an object.

When creating ActiveX controls that are wrappers for VCL controls, the Delphi wizards automatically handle most of the steps listed above. The Delphi wizards also simplify the process of creating ActiveForm and OLE Automation objects.

Note:	While a CoClass is a class that consists only of a set of interface implementations, a Delphi class that implements interfaces need not be limited to the methods declared in the interfaces. The Delphi class may also contain other data and methods. However, the additional data and methods are not accessible through any of the interface references to the object instantiated as a CoClass.


Creating classes that support interfaces

Section topics 	See also

Delphi defines a simple class, TInterfacedObject, that can be used as a base class when implementing COM-compatible interfaces that are internal to your application. TInterfacedObject is not technically a CoClass, since COM requires a registered class identifier (CLSID) and the ability to be externally instantiated with a class factory. Thus TInterfacedObject can be used as a base class when writing objects that are internally instantiated either within a single module or in an in-process client (external to the application but in the same process space).

TInterfacedObject serves as a convenient base class for creating interfaced objects because it implements the methods of IUnknown. TInterfacedObject class is declared in the System unit as follows:

type

 TInterfacedObject = class(TObject, IUnknown)

 private

   FRefCount: Integer;

protected

   function QueryInterface(const IID: TGUID; out Obj): Integer; stdcall;

   function _AddRef: Integer; stdcall;

   function _Release: Integer; stdcall;

 public

   property RefCount: Integer read FRefCount;

 end;

Deriving directly from TInterfacedObject is straightforward. In the following example declaration, TDerived is a direct descendent of TInterfacedObject and implements a hypothetical IPaint interface.

type

 TDerived = class(TInterfacedObject, IPaint)

   ...

 end;


Using classes that support interfaces

Section topics 	See also

TInterfacedObject, or any of the new classes that implement interfaces, have the advantage of dynamic-binding providing true polymorphism on the interface. For example, given the following declarations:

var

 X: IPaint;

 P: TInterfacedObject;

begin

 P := TInterfacedObject.Create;

 X := P;

the variable P of type TInterfacedObject, can be assigned to the variable X, which is an IPaint interface reference. Dynamic binding makes this assignment possible. For this assignment, the compiler generates code to call the QueryInterface method of P's IUnknown interface, because the complier cannot tell from P's declared type whether P's instance actual supports IPaint. At run-time P resolves to an IPaint reference because of the call to QueryInterface, implemented by TInterfacedObject. P can then be assigned to X without error.

When VCL classes that do not implement interfaces are used as a base class for a class that implements interfaces, such an assignment will cause a compile-time error. For example:

var

 X: IPaint;

 P: TObject;

begin

 P := TObject.Create;

 X := P;	// incompatible types!

will produce a compile error because P's declared type implements neither IUnknown or IPaint. In this case the IPaint interface has to be statically resolvable at compile-time because the QueryInterface method is not available in the base class. For more information about such assignment rules regarding interfaces, see Object Pascal Language Guide.

Another advantage of using a class that implements IUnknown as a base is the versioning capabilities of QueryInterface. When a software module is updated QueryInterface can be invoked at runtime to query the current capabilities of an object that has implemented this method. New versions of old interfaces, or any new interfaces or features of an object become immediately available to new clients; at the same time the object retains complete compatibility with existing client code, without requiring any recompilation of clients. Moreover, because the implementation of an interface is completely hidden while the methods and parameters remain constant, a developer can change the implementation to improve performance, or for any internal reason, without breaking any client code that relies on that interface.


Interfaces and code reuse

Section topics 	See also

Interfaces are useful when sets of operations, such as streaming, are used across a broad range of objects. By using interfaces such as IStream, or a data-aware interface, objects can reuse code and consistently apply it to a variety of different applications. These types of interfaces can be used among a number of Windows controls objects; they can be shared between objects written in Delphi, C++, and other languages; and they can be updated without affecting the users of those objects.

Sharing interfaces between classes

The following example demonstrates how to use interfaces to separate how a class is used from how it is implemented. Two classes can share the same interface without being required to descend from the same base class. This polymorphic invocation of the same methods on unrelated object as long as the objects implement the same interface. For example, consider the interface:

IPaint = interface

  procedure Paint;

end;

and the two classes,

TSquare = class(TInterfacedObject, IPaint)

  procedure Paint;

end;

TCircle = class(TInterfacedObject, IPaint)

  procedure Paint;

end;

Even if the two classes did not share a common ancestor, they would still be assignment compatible with a variable of IPaint as in:

var

  Painter: IPaint;

begin

  Painter := TSquare.Create;

  Painter.Paint;

  Painter := TCircle.Create;

  Painter.Paint;

end;

This could have been accomplished by having TCircle and TSquare descend from say, TFigure which implemented a virtual method Paint. Both TCircle and TSquare would then have overridden the Paint method. The above IPaint would be replaced by TFigure. However, consider the following the following interface:

IRotate = class

  procedure Rotate(Degrees: Integer);

end;

which makes sense for the rectangle to support but not the circle. The classes would look like:

TSquare = class(TInterfacedObject, IPaint, IRotate)

  procedure Paint;

  procedure Rotate(Degrees: Integer);

end;

TCircle = class(TInterfacedObject, IPaint)

  procedure Paint;

end;

Later, you could create a class TFilledCircle that implements the IRotate interface to allow rotation of a pattern used to fill the circle without having to add rotation to the simple circle.

Using interfaces with procedures

Interfaces also allow you to write procedures that can generically handle objects without having to require the object to descend from a particular base class. Using the above IPaint and IRotate interfaces you can write the following procedures:

procedure PaintObjects(Painters: array of IPaint);

var

  I: Integer;

begin

  for I := Low(Painters) to High(Painters) do

    Painters[I].Paint;

end;

procedure RotateObjects(Degrees: Integer; Rotaters: array of IRotate);

var

  I: Integer;

begin

  for I := Low(Rotaters) to High(Rotaters) do

    Rotaters[I].Rotate(Degrees);

end;

RotateObjects does not require that the objects know how to paint themselves and PaintObjects does not require the objects know how to rotate.  This allows the above objects to be used more often than if they where written to work only against a TFigure class.

Using interfaces with generic algorithms

Interfaces also allow the implementation of other generic algorithms such as sorting. The following introduces an ICompare interface and a QuickSort procedure that uses the ICompare interface to sort an array of TObjects.

ICompare = interface

  function CompareWith(Object: TObject): Integer;

end;

...

procedure QuickSort(Objects: array of TObject);

  procedure Sort(Objects: array of TObject; Left, Right: Integer);

  var

    I, J: Integer;

    Object, Temp: TObject;

    Compare: ICompare;

  begin

    repeat

I := Left;

J := Right;

Object := Objects[(Left + Right) div 2];

Compare := Object as ICompare;

repeat

  while Compare.CompareWith(Objects[I]) < 0 do Inc(I);

  while Compare.CompareWith(Objects[J]) > 0 do Inc(J);

  if I <= J then

  begin

    Temp := Objects[I];

    Objects[I] := Objects[J];

    Objects[J] := Temp;

    Inc(I);

    Inc(J);

  end;

      until I > J;

if Left < J then Sort(Objects, Left, J);

Left := I;

    until I >= Right;

  end;

begin

  Sort(Objects, Low(Objects), High(Objects));

end;

The above routine could have been implemented without interfaces but would have required all objects to share a common ancestor that implements the CompareWith method. This is not always possible. You might not want to, or have the access to, change the ancestors of all the classes you wish to sort. Interfaces allow you to create descendent classes for the classes you wish to support that all implement ICompare without requiring the their ancestors to be modified.


OLE and ActiveX objects

Section topics 	See also

The OLE and ActiveX extensions to COM adhere to the COM specification, which requires objects to be instantiated from a CoClass. CoClasses require a unique identifier (CLSID) and a class factory.

Using OLE and ActiveX objects introduces both features and restrictions. These objects can be visual or nonvisual, and they can run in the same or different process spaces from their clients. When making cross-process or remote procedure calls, the parameters must be packaged and unpackaged. This process, of packaging and sending the parameters of interface methods across process boundaries separating client and server applications, is called marshaling. Be aware, when working with remote and cross-process applications, that different parameter types are marshaled in different ways, and that different types of OLE and ActiveX objects have different ways of handling the marshaling process.

The following table summarizes the types of OLE objects that you can create, whether or not they are visual, what process spaces they can run in, how they handle marshaling, and whether or not they require a type library.

OLE/ActiveX object requirements:

OLE object	Visual	Process space	Communication	Type library
OLE/Active Document	Usually	In-process or cross-process	Verbs for marshaling	No
OLE Automation	Occasionally	In-process, cross-process, or remote	IDispatch auto-marshaling	Recommended
ActiveX control	Usually	In-process	IDispatch auto-marshaling	Required
Custom interface object	Optional	In-process, cross-process, remote	Manual marshaling	Recommended


OLE/Active Documents

Section topics 	See also

One set of services that OLE provides through its communication, linking, and embedding interfaces is OLE Documents. These objects have the OLE features of linking and embedding, drag-and-drop, visual editing, and in-place-activation. To work with OLE Documents you would implement any of a number of interfaces that provide standardized behavior for them, such as drag-and-drop and visual editing. By implementing these interfaces, OLE Documents can seamlessly incorporate data, or objects, of different formats such as sound clips, spreadsheets, text and bitmaps.

Unlike ActiveX controls, OLE Documents are not limited to in-process servers, but can be used in cross-process applications. Unlike OLE Automation objects, which are almost never visual, OLE document objects can be visually active in another application. For this purpose OLE Document objects are associated with two types of data: presentation data, used for visually displaying the object on a display or output device, and native data, used to edit the object.

OLE documents objects can be document containers or document servers. Delphi has a class, TOleContainer, that can be used as the base for an OLE document container. To create objects for OLE document servers you can use one of the Delphi COM base classes and implement the appropriate interfaces for that object type, depending upon the services the object needs to support. For more information about creating and using OLE Document servers, see the Microsoft Web site.

Note:	While the OLE specification for OLE Documents has built-in support for marshaling for cross-process applications, OLE Documents do not run on remote servers because the types it uses, such as window handles, menu handles, and so on, are specific to a system on a given machine.


OLE Automation

Section topics 	See also

OLE Automation refers to the ability of an application to control the objects in another application, programmatically. The client of an OLE Automation object is referred to as an OLE Automation controller, and the server object being manipulated is called the OLE Automation object. OLE Automation can be used on in-process, local, and remote servers.

OLE Automation is characterized by two key points:

The OLE Automation object must be able to define a set of properties and commands, and to describe their capabilities through type descriptions. In order to do this they must have a way to provide information about the object's interfaces, the interface methods, and those methods' arguments. Typically this information is available in type libraries. In Delphi the Automation server can also generate type information on-the-fly when queried for type information.

OLE Automation objects must make these methods accessible so that other applications can use them. For this they must implement the IDispatch interface. Through this interface an object can expose all of its methods and properties. Through the primary method of this interface, the object's methods can be invoked, once having been identified through type information.

Developers wanting to create and use non-visual OLE objects that can run in any process space can use OLE Automation. One of the reasons for this is that OLE Automation is a mechanism that provides an automatic way to allow cross-process applications to communicate by implementing the IDispatch interface, which automates the marshaling process. OLE Automation does, however, restrict the types that you can use. For more information about OLE Automation compatible types, see "OLE and ActiveX Overview," and for information about using these types in type libraries, see "Working with type libraries."

The Delphi class, TAutoObject, implements IDispatch and can be used as a base class for creating OLE Automation objects. The characteristic features of OLE Automation form the foundation for ActiveX controls. Therefore, in Delphi TAutoObject is an ancestor of Delphi ActiveX base classes. Delphi OLE Automation and ActiveX classes also support dual interfaces. For an overview of the ActiveX hierarchy, see "Delphi ActiveX Framework." For more information about dual interfaces, see "Dual interfaces."


ActiveX controls

Section topics 	See also

The ActiveX technology extends COM and OLE to support new features required for ActiveX controls. ActiveX controls are visual controls that run on in-process servers, and can be plugged into an OLE container application. They are not complete applications in themselves, but can be thought of as prefabricated OLE controls that are reusable in various applications. ActiveX controls make use of OLE Automation to expose their properties, methods, and events. Features of ActiveX controls include the ability to fire events, bind to data sources, and support licensing. Delphi wizards allow you to easily creating ActiveX controls as well as ActiveForms. For more information about creating and using these types of objects, see "Creating ActiveX controls" and "Creating ActiveForms."

An increasingly common use of ActiveX controls is on a Web site as interactive objects in a Web page. As such, ActiveX has become standard that has especially targeted interactive content for the World Wide Web, including the use of ActiveX Documents used for viewing non-HTML documents through a Web browser. For more information about ActiveX technology, see the Microsoft Web site. For more information about deploying Delphi ActiveForms and ActiveX controls on a Web site, see "ActiveX Web Deployment."


Visual cross-process objects

Section topics 	See also

OLE Automation objects, OLE Documents and ActiveX controls are commonly used objects. Less common are OLE/ActiveX objects that are visually displayed and manipulated in a cross-process application. These types of objects are more difficult to create because the communication protocol involved in visually manipulating objects in cross-process applications is standardized only for visual objects that use OLE Document interfaces. Therefore, you would need to write one or more custom interfaces that your object implements, and then handle the marshaling interfaces yourself. This can be done in by either:

Using a dual interface IDispatch, which can be automarshaled. This is the recommended way. It is the easiest approach, and Delphi creates dual interfaces by default when you create an automation object (a descendent of TAutoObject).

Writing the marshaling classes by hand.


OLE Automation interfaces

Section topics 	See also

OLE Automation servers are objects that implement the IDispatch interface. Because IDispatch derives from IUnknown, they also implement the IUnknown interface. IUnknown manages all of the other interfaces that the server object supports, while the methods of the IDispatch interface provide access to the methods and properties of the OLE Automation object. The following figure shows the vtable for the IDispatch interface in an object that supports the IUnknown and IDispatch interfaces:

Dispatch interface vtable

OLE Automation controllers are clients that use the OLE IDispatch interface to access the OLE server objects that implement this interface. The controller must first create the object, then query the object's IUnknown interface for a pointer to its IDispatch interface. OLE Automation controllers can access OLE Automation objects in two different ways:

By using the IDispatch interface.

By directly calling one of the member functions in the object's virtual function table (vtable) if a dual interface was defined.


Dispatch interfaces

Section topics 	See also

Dispatch interfaces are specific implementations of IDispatch. While programmers use the names of objects, methods, and properties, IDispatch keeps track of methods and properties internally by a dispatch identifier (dispID), which is unique identification number for an interface member. Before an OLE Automation controller can find and access a property or method, it must have the dispID that maps to the name of the interface member. These dispIDs are available at runtime, and controllers get them by calling the IDispatch method GetIDsOfNames. Through IDispatch, a controller retrieves the object's type information for the dispatch interface and then maps interface member names to specific dispIDs. This is called late binding because the controller binds to the property or method at runtime rather than at compile time.

Once it has the dispID, the controller can then call the IDispatch method Invoke to execute the appropriate code (property or method), packaging the parameters for the property or method into one of the Invoke parameters. Invoke has a fixed compile-time signature that allows it to accept any number of arguments when calling an interface method. This includes named and optional arguments. The Automation object's implementation of Invoke must then unpackage the parameters, call the property or method, and be prepared to handle any errors that occur. When the property or method returns, the object passes its return value back to the controller.

Advantages and disadvantages when using IDispatch

A major advantage of using IDispatch is that it has built-in support for marshaling, which is the communication process between applications running in different process spaces. When you use IDispatch the marshaling process of transferring date is automatic. This means that no additional proxy/stub implementations are required by the programmer. For client applications you do not need to write the stub code to unpackage the parameters and call a method on the server; and for server applications you do not need to write the proxy code to package the method's parameters for marshaling across a process boundary.

A disadvantage of using dispatch interfaces is that it limits the types of parameters that can be used by your interface to a basic 13 types. This can severely restrict user-defined structures. For more information on the types supported by dispatch interfaces, see "Creating OLE Automation servers."

It is important to be aware that a type library is not required for OLE Automation objects because GetIDsOfNames binds names to dispIDs at runtime. Consequently, there is no compile-time type checking so it is possible for Invoke to generate runtime errors. However, in Delphi all OLE Automation objects created with the wizards will have type libraries associated with them, thereby avoiding this problem.

ID binding

In some circumstances you can use compile time binding on dispatch interfaces. If the object is described in a type library, an OLE Automation controller can read the dispIDs from the type library at compile time because the dispID of each property or method is fixed and is part of the object's type description. When this happens it is called ID binding. Because GetIDsOfNames is logically called at compile time in this case, ID binding is actually a form of early binding. Because ID binding requires only one call to IDispatch at runtime (that is, the call to Invoke), rather than the two calls required by late binding, it is about twice as fast. An advantage to ID binding is that you get better compile-time type checking, and you receive more informative error messages. The disadvantage to using this method is that there is no way to check to see if a method name has changed, because once GetIDsOfNames binds dispIDs to names, Invoke refers to them only by number.

Dispinterfaces

Delphi uses dispinterfaces instead of ID binding to take advantage of early binding on dispatch interfaces. Dispinterfaces do not use GetIDsOfNames to bind the member to the dispID. Rather, if you import a type library that contains the given interface, Delphi will generate the dispIDs for you and associate them with their corresponding member for the dispinterface. The following example illustrates the syntax for a dispinterface definition:

IButtonXEvents = dispinterface

   ['{8F9C8E22-7FA2-11D0-8080-0020AF74DE39}']

   procedure OnClick; dispid 1;

   procedure OnKeyPress(var Key: Smallint); dispid 2;

end;

Dispinterfaces have the speed advantage of ID binding, and can be called by readable, text-based names rather than by dispID values.

The dispinterfaces exists so that users can make late-bound automation calls to an automation server, such as, an ActiveX control, while still being able to take advantage of CodeInsight. (CodeInsight cannot help you when you make automation calls via a variant). Users will rarely need to use the dispinterface directly because the vtable interface is a better-performing alternative.


Dual interfaces

Section topics 	See also

OLE Automation allows an OLE Automation controller to call a method or property accessor function directly using vtable binding, rather than the IDispatch interface, to access the Automation object. However, some controllers will be able to access the object only at runtime through IDispatch. These include interpreted macro-programming languages and scripting tools that must access and manipulate objects, but that do not having compile-time knowledge of vtable layouts. For this reason, OLE Automation defines what is called a dual interface, which is a combination of a dispatch interface and a vtable interface. Dual interfaces expose the properties and methods both through IDispatch and also directly through the vtable.

Vtable interfaces

Vtable interfaces are custom, or user-defined, interfaces that allow clients to invoke interface methods based upon their order in the vtable and knowledge of the argument types. The vtable lists the addresses of all the properties and methods that are members of the object, including the member functions of the interfaces that it supports. The first three members of the vtable are the members of the IUnknown interface. Subsequent entries are the members of the other supported interfaces. If the object does not support IDispatch, the entries for the members of the object's custom interfaces immediately follow the members of IUnknown. The diagram "Interface vtable" shows the layout for this type of vtable interface.

If a client can obtain a type library for the object, it can get the dispIDs of an IDispatch interface and bind directly to a vtable offset. This compile-time binding generates code to call any of the object's methods or properties directly through that object's vtable, thereby avoiding calls to GetIDsOfNames and Invoke. Because access is direct through vtable binding and no calls are made through IDispatch, this method is faster than both ID binding and the late binding of dispatch interfaces.

Defining a dual interface

A dual interface is a COM vtable interface that derives from IDispatch. Consequently all of the parameters and return types specified for members of a dual interface must be compatible with OLE Automation types. The following is an example of a definition for a dual interface:

IProvider = interface(IDispatch)

   ['{6E644935-51F7-11D0-8D41-00A0248E4B9A}']

   function GetData: Variant; safecall;

   function ApplyUpdates(Delta: Variant): Variant; safecall;

   function GetMetaData: Variant; safecall;

   function GetRecords(Count: Integer; var HitEOF: WordBool): Variant; safecall;

   function DataRequest(Input: Variant): Variant; safecall;

   function Reset: WordBool; safecall;

 end;

A dual interface is therefore a custom interface and a dispinterface at the same time.

The first three entries of the vtable for a dual interface are the members of IUnknown, the next four entries are the members of IDispatch, and the remaining entries are COM entries for direct access to members of the dual interface. The following diagram shows the vtable for the IMyInterface interface in an object that supports a dual interface named IMyInterface:

Dual interface vtable

Advantages of using dual interfaces

Dual interfaces offer the combined advantages of vtable interfaces and dispatch interfaces. These advantages are:

Dual interfaces allow clients written in compiled languages, that know the names of automation object members, to perform vtable binding, compiling directly against the function pointers in the vtable.

Dual interfaces provide the high-speed advantages of custom interfaces by calling directly through the vtable for in-process dual objects. Therefore you can still get the efficiency of a custom interface when accessing in-process objects.

Dual interfaces have the advantage of early binding that gives you compile-time type checking.

Dual interfaces allow automation controllers that cannot obtain type information, and that can call only IDispatch members, to access your object.

Dual interfaces have the added benefit that OLE Automation provides, which is auto-marshaling for both IDispatch and custom portions of the interface. When you have an out-of-process object, you benefit from auto-marshaling. The system provides a universal proxy/stub implementation that can marshal the interface based on the information contained in a type library.

Restrictions when using dual interfaces

For an interface to be a dual interface it must derive from IDispatch and it must use OLE Automation compatible types. These type restrictions are required for OLE's auto-marshaling support to work properly. This means that:

A dual interface has the restrictions imposed by automation compatibility: no data structures (other than implementing another property object), no unsigned arguments, having to use wide strings, and so on.

All members of a dual interface must pass an HRESULT as the function's return value.

Members of a dual interface that need to return other values should specify the last parameter as var or out, indicating an output parameter that returns the value of the function.

For more information about OLE Automation compatible types, see "OLE and ActiveX Overview," and for information about using these types in type libraries, see "Working with type libraries."

Note:	One way to bypass the OLE Automation types restrictions, is to separately implement an IDispatch interface and a custom interface. By defining a custom interface, that is not combined with a dispatch interface, you can use the full range of possible argument types. This means that COM clients have the option of using the custom interface, which OLE Automation controllers can still access, but in a less efficient manner.

Dual interfaces in Delphi

Dual interfaces have significant advantages over both IDispatch-only and vtable-only interfaces because existing OLE Automation controllers that use the late-binding IDispatch interface will continue to work. Because of their advantages, dual interfaces are recommended to use whenever possible. Delphi generates dual interfaces by default for OLE Automation server objects, and as client applications Delphi Automation controllers will take advantage of using the vtable when accessing OLE Automation servers.


Delphi ActiveX Framework

Section topics 	See also

The Delphi ActiveX Framework (DAX) enables you build ActiveX controls from VCL controls. The DAX supports implementing OLE automation servers as well as ActiveX controls that can be embedded in a variety of containers, including:

Internet Explorer 3.0

Delphi 2.0

Borland C++ Builder

Visual C++ 4.x

Visual Basic 4.0 and 5.0

ActiveX Control Pad

The classes in the DAX implement COM objects, including ActiveX controls, non-visual OLE automation servers, and ActiveX property pages. In the case of ActiveX controls, work is divided between two separate Object Pascal class types: the Windows implementation and the OLE controller object. The developer implements a standard VCL control descended from TWinControl (such as TPanel) and an OLE controller object derived from TActiveXControl. The OLE controller object keeps a pointer to the Windows implementation object, which it creates, manipulates,   and destroys in response to run-time events. The Windows implementation object forwards events from the UI to the controller object, which may in turn forward those events to its site.

The DAX class hierarchy includes the following objects:

TComObject  TTypedComObject  TAutoObject  TActiveXControl

An ActiveX control is a class descended from TActiveXControl.

Each base class in the DAX hierarchy provides standard behavior for a specific type of COM or OLE object by implementing the appropriate interfaces. Classes in the hierarchy build upon one another mirroring the COM technologies of OLE Automation and ActiveX as they extend COM. The following is a summary of the inherited functionality for the ActiveX control. For more detailed information about any of the above classes, see individual entries in Visual Component Library Reference. For more information about using these classes with the Delphi wizards to easily create ActiveX controls, see "Creating ActiveX controls."


TComObject

Section topics 	See also

TComObject is an abstract class that defines the core behavior and interfaces required for a COM object. TComObject has a CLSID and a class factory, so it can be registered and externally instantiated (instantiated from another module). In addition to providing the functionality for a simple COM object, TComObject also supports aggregation, OLE exception handling, and the safecall calling convention used for dual interfaces.

Developers would typically use TComObject as a base class for implementing custom interfaces, or Win95 Shell extensions. TComObject does not support the typeinfo interfaces used to access a type library.


TTypedComObject

Section topics 	See also

TTypedComObject adds one important feature to TComObject, that is, the ability to access (load and read) type libraries. For this reason TTypedComObject and all of its descendents must provide typeinfo descriptions in a type library. TTypedComObject supports the IProvideClassInfo interface. Because of this interface, instances of TTypedComObject can also provide type information without the client having to obtain a type library.


TAutoObject

Section topics 	See also

TAutoObject is a class that implements the IDispatch interface, thereby defining the core behavior required for OLE Automation. TAutoObject is used for OLE Automation objects that must support dispatch interfaces; and because ActiveX is based on OLE Automation, TAutoObject is an ancestor class for the Delphi ActiveX control class.

TAutoObject contributes auto-marshaling capabilities to the DAX hierarchy. Developers can use TAutoObject as a base when deriving classes for which they want auto-marshaling capabilities. When creating objects that must run cross-process developers who do not use the auto-marshaling feature of OLE Automation will need to implement marshaling themselves. For more information about auto-marshaling and tips for manual marshaling, see "OLE and ActiveX objects" and "Advantages and disadvantages when using IDispatch."


TActiveXControl

Section topics 	See also

TActiveXControl is the abstract class used to create ActiveX controls in Delphi. TActiveXControl descends from TAutoObject, and in the DAX hierarchy inherits all of the functionality of its ancestor classes. The resulting object is a COM object that supports OLE automation and includes the ability to access type information. All ActiveX controls require a type library. In addition, the TActiveXControl class implements the following behaviors:

embedding in a container (site)

in-place activation

persistence

events

property pages

property browsing

For more information about creating ActiveX controls, see "Creating ActiveX controls.".


See also

TInterfacedObject

TComObject

TTypedComObject

TComObjectFactory

TTypedComObjectFactory

TAutoObject

TActiveXControl

TActiveForm

TOleContainer

ActiveX components

Creating ActiveX controls

Creating OLE Automation servers


Creating ActiveX controls

Section topics 	See also

These topics describe how to create ActiveX controls and how to use the ActiveX control wizard to convert a VCL control into an ActiveX control.

An ActiveX control is a software component that can be integrated into any host that supports ActiveX controls such as Paradox for Windows, Delphi, Delphi applications, Visual dBASE, Borland C++ Builder, Access, Visual Basic and Internet browsers such as MS Internet Explorer. An ActiveForm is a specialized ActiveX control used for Web deployment. For information on how to create an ActiveForm, see "Creating ActiveForms."


Overview of ActiveX control creation

Section topics 	See also

To create a new ActiveX control:

1.	Design and create the VCL control that forms the basis of your ActiveX control. For information on this, see "Overview of component creation."

2.	Use the ActiveX control wizard to create an ActiveX control from the VCL control.

3.	Use the ActiveX property page wizard to create a property page for the control (optional).

4.	Associate the property page with the ActiveX control (optional).

5.	Compile and register the control.

6.	Test the control with all potential target applications.


Elements of an ActiveX control

Section topics 	See also

An ActiveX control involves many elements which each perform a specific function. The elements include a VCL control, properties, methods, events, and one or more associated type libraries.

VCL control

An ActiveX control in Delphi is simply a VCL control that has been made accessible to applications and objects that support ActiveX controls. When you create an ActiveX control, you must first design or choose the VCL control from which you will make your ActiveX control.

Type library

The type library contains the new type definitions for the control and can be created automatically by the ActiveX control wizard. This type information provides a way for host applications to find out what the control can do. For more information about type libraries, see "Type libraries.".

When you design your control, type library information is stored in a .TLB file and a corresponding Pascal file containing the Pascal translations. When you build the ActiveX control, the type library information is automatically compiled into the OCX or EXE as a resource.

Properties, methods, and events

The properties, events, and methods of the VCL control become those of the ActiveX control.

A property is an attribute, such as a color or label.

A method is a request to a control to perform some action.

An event is a notification from a control to the container that something has happened.

Property page

The property page allows the user of a control to view and edit its properties. You can group several properties on a page, or use a page to provide a dialog-like interface for a property. For information on how to create property pages, see "Creating property pages."


Creating an ActiveX control

Section topics 	See also

ActiveX controls are created when the ActiveX control wizard automatically puts an ActiveX wrapper around a VCL control and builds a OCX that contains the object. ActiveX defines the protocol for object communication. This ActiveX wrapper exposes the capabilities of the control to other objects and servers.

Before using the ActiveX control wizard, you must select which VCL control from which to build the ActiveX control.


Using the ActiveX control wizard

Section topics 	See also

To create an ActiveX control:

1.	Choose File|New to open the New Items dialog box.

2.	Choose the ActiveX page. The ActiveX page appears.

3.	Select the ActiveX Control icon and Choose OK. The ActiveX Control Wizard dialog appears.

4.	Select a control from the VCL Class Name drop-down list.

The wizard fills in default names for ActiveX Name and Implementation Unit, which you can change. The implementation unit contains the code that implements the behavior of the ActiveX control.

ActiveX controls must be added to an ActiveX library project. If you don't currently have an ActiveX Library project open, a fourth field allows you to specify which ActiveX Library project you want to add the ActiveX control. A default Project Name is provided.

5.	There are three ActiveX Control Options that you can set:

Include Design-Time License

A design-time license ensures that users of a control cannot open the control for design purposes unless they have the license key for the control. With Include Design-Time License checked, the wizard creates a key for the control which is stored in a .LIC file the same name as the project. The user of the control must have a copy of the .LIC file to open the control in a development environment. Each control in the project that has Include Design-Time Licence checked will have a separate key entry in the .LIC file.

Include Version Information

This option includes version information in the .OCX file. Adding this resource to your control allows your control to expose information about the module, such as copyright and file description, which can be viewed in the browser. Version information can be specified by choosing Project|Options and selecting the VersionInfo page. For information on the individual settings on this page, see on-line Help.

Note:	Version information is required for registering a control in Visual Basic 4.0.

Include About Box

When this box is checked, an about box is included in the project. The user of the control can display the about box in a development environment. The about box is a separate form and that you can modify. By default, the about box includes the name of the ActiveX control, an image, copyright information, and an OK button.

6.	Choose OK.

The wizard creates an ActiveX Library project if needed, a type library, a form, an implementation unit, and a unit that contains all of the corresponding type library declarations. If Include About Box was checked, an about box form and unit are created and added to the project.

The wizard generates the implementation unit and implements all properties, methods, and events of the VCL control in the ActiveX control. You can modify the member implementations in this file to suit your needs.


Editing the type library

Section topics 	See also

The type library defines all information about data types, interfaces, member functions, and object classes that the ActiveX control will expose. The name of the new type library created by the wizard is the project name with a .TLB extension. The Type Library editor is a convenient tool for specifying an interface's properties and methods.

To edit the type library with the Type Library editor:

1.	Choose File | Open

2.	Enter the name of the .TLB file and choose Open.

This brings up the Type Library editor.

3.	At this point you can add properties and methods to the type library that either were not present in the VCL control, or were not automatically converted.

Note:	Since Active ActiveX controls have a different mechanism for making controls data-aware than a VCL control, Delphi does not convert the properties related to data. You may want to add some of the ActiveX data aware properties to your control.

The wizard creates a dual interface that defines the properties and methods for the control, and a dispatch interface that specifies which events can be fired by the control. for more information on dual and dispatch interfaces, see "OLE Automation interfaces."

For more information on type libraries and the Type Library editor, see "Working with type libraries."


Adding additional members to the interface

Section topics 	See also

To add additional properties, methods, and events to the control:

1.	Choose Edit | Add To Interface. The ActiveX controls implementation unit must be open and selected for the menu item to be available.

This brings up the Add to Interface dialog box.

2.	Choose Method or Event from the Interface drop-down list. Next to Properties/Methods or Events is the name of the interface to which the member will be added.

3.	Enter the declaration for the property, method, or event. For example, if you were creating an ActiveX control with the TButton VCL control, you could add the following property:

property Caption:WideString

If you check the Syntax helper check box, pop-up windows appear as you type, prompting you for what is expected at each point.

4.	Choose OK.

The declaration is automatically added to the control's implementation unit, type library (.TLB) file, and type library unit.

Adding properties

Because the interface is a dual interface, the properties are implemented using read and write access methods in the .PAS file. The wizard would actually add the following declaration for the preceding Caption property to the implementation unit:

property Caption: WideString read Get_Caption write Set_Caption;

The read and write access method declarations and implementations for the property would be

function Get_Caption: WideString; safecall;

procedure Set_Caption(Value: WideString); safecall;

function TButtonX.Get_Caption: WideString;

begin

  Result := WideString(FDelphiControl.Caption);

end;

procedure TButtonX.Set_Caption(Value: WideString);

begin

  FDelphiControl.Caption := String(Value);

end;

These methods are declared as safecall, meaning they automatically implement OLE conventions for errors and exception handling.

The interface implementation methods simply pass through the behavior to the VCL control.  In some cases, as in the preceding example, you may need to add code to convert the OLE data types to native Delphi types.

Adding methods

When you add a method, an empty implementation is added for you to add its functionality. For example, if you added

procedure Move;

the following declaration and code would be added to the unit:

procedure Move; safecall;

procedure TButtonX.Move;

begin

end;

Note:	Because the automation interface methods are declared safecall, you do not have to implement OLE exception code for these methods--the Delphi compiler handles this for you by generating code around the body of safecall methods to catch Delphi exceptions and convert them into OLE error info structures and return codes.

Adding events

An ActiveX control can fire events to its container. You add events to specify which events can be fired by the control. When you add an event, the following items are created:

a dispatch interface declaration and implementation entries in the implementation unit,

procedure KeyPressEvent(Sender: TObject; var Key: Char);

procedure TButtonX.KeyPressEvent(Sender: TObject; var Key: Char);

var

 TempKey: Smallint;

begin

 TempKey := Smallint(Key);

 if FEvents <> nil then FEvents.OnKeyPress(TempKey);

 Key := Char(TempKey);

end;

a type library entry for the event interface with dispinterface checked on its attributes page, and

an Object Pascal version of the type library's interface source file.

Unlike the automation interface, the events interface is not added to the control's interface list.  The control does not receive these events--the container does.


Adding a property page

Section topics 	See also

Property pages can be added that enable the user of the control to update and edit attributes of the control. For information on how to create a property page, see "Creating property pages."

Once the property page has been created, it must be associated with the ActiveX control.  For example, here's how you would associate a property page with an ActiveX button control:

1.	To the DefinePropertyPages method implementation in the controls implementation unit, add DefinePropertyPage with the GUID of the property page as the parameter. The GUID for the property page can be found in the property pages unit, for example:

procedure TButtonX.DefinePropertyPages(DefinePropertyPage: TDefinePropertyPage);

begin

  DefinePropertyPage(Class_PropertyPage1);

end;

2.	Add the property page unit to the uses clause of the controls implementation unit.

3.	In the property page unit, add code to the UpdatePropertyPage method that will update the property page with the controls data. For example, if the edit box on a property page contains the text for the Caption property of the control, the following code would ensure that the edit box is updated when the control's Caption property changes.

procedure TPropertyPage1.UpdatePropertyPage;

begin

 Edit1.Text := OleObject.Caption

end;

4.	In the property page unit, add code to the UpdateObject method that will update the control with the latest property page data. For example, if the edit box on a property page contains the text for the Caption property of the control, the following code would ensure that the control is updated with the data in the property page when the user chooses the Apply button on the property page.

procedure TPropertyPage1.UpdateObject;

begin

 OleObject.Caption := Edit1.Text;

end;


Building and registering the server

Section topics 	See also

Building the project creates an .OCX file that contains the ActiveX control. You must register the control so that it can be used by other applications such as Visual Basic, Delphi applications or Paradox for Windows. Registering the control adds an entry for it in the Windows registry.

To register the ActiveX control, choose Run|Register ActiveX Server.

When you wish to remove the ActiveX control from your system, it is recommended that you first remove its entry from the Windows registry.

To unregister the ActiveX control, choose Run|Unregister ActiveX Server.


Testing the ActiveX control

Section topics 	See also

To test your control in Delphi, add it to a package and import it as an ActiveX control. This adds it to the control palette. You can drop the control on an application's form and test as needed.

Your control should also be tested in all target applications that will use the control.

To debug the ActiveX control, select Run | Parameters and type the client name in the Host Application edit box.

The parameters then apply to the host application. Selecting Run | Run will run the host or client application and allow you to set breakpoints in the control.


Deploying the ActiveX control

Section topics 	See also

An ActiveX control can be deployed to your Web server. In order to deploy the ActiveX control project, Web deployment options must be set appropriately, and the project must be compiled. For more information on the options in this dialog, see "ActiveX Web Deployment."


See also

TActiveXControl

ActiveX components

OLE and ActiveX Overview

Working with type libraries

ActiveX Web Deployment

Creating ActiveForms

Overview of component creation

Creating property pages


Creating ActiveForms

Section topics 	See also

These topics describe how to create an ActiveForm. An ActiveForm is a Delphi form that is also an ActiveX control. When an ActiveForm is Web deployed, an HTML page is created to contain the reference to the ActiveForm and specify its location within the page. The Activeform is then displayed and run from within Web browsers such as Microsoft Internet Explorer 3.0 and Netscape Navigator 4.0. Inside the Web browser the form behaves just like a stand-alone Delphi Form. The form may contain any VCL or ActiveX components, including custom built VCL controls.

Delphi provides an ActiveForm wizard to facilitate the creation of ActiveForms. The wizard creates

an ActiveX library project to contain the ActiveForm

an active form which you can customize

After you have designed and compiled the ActiveForm project into an ActiveX library, you can deploy the project to your Web server and Delphi will create a test HTML page with a reference to the ActiveForm.


Creating an ActiveForm

Section topics 	See also

To create an ActiveForm:

1.	Choose File|New to open the New Items dialog box.

2.	Tab to the ActiveX page which opens with the ActiveForm icon highlighted

3.	Choose OK. The ActiveForm Wizard dialog appears.

The wizard fills in default names for ActiveX Name and Implementation Unit, which you can change. The implementation unit contains the code that implements the behavior of the ActiveForm.

Activeforms must be added to an ActiveX library project. If you currently don't have an ActiveX Library project open, a fourth field allows you to specify which ActiveX Library project to add the ActiveForm control. A default Project Name is provided.

4.	There are three ActiveX Control Options that you can set.

Include Design-Time License

Include Version Information

Include About Box

See "Creating ActiveX controls" for a description of these options.

5.	Choose OK.

The wizard creates an ActiveX Library project if needed, a type library, a form, an implementation unit, and a unit that contains all of the corresponding type library declarations. If Include About Box was checked, an about box form and unit are created and added to the project.

Delphi generates the implementation unit and implements the properties, methods, and events of the TActiveForm in the ActiveForm.

6.	If the current project is not an ActiveX library, a warning asks if you wish to create a new ActiveX library project.

An active form is contained in an ActiveX Library; therefore, an ActiveX library project must first be created to contain the active form.

7.	Choose OK.

Delphi adds a blank form to your ActiveX library project and creates the ActiveX library project if needed. At this point you can add controls and design the form as you wish. For information on building forms, see the User's Guide.


Publishing properties

Section topics 	See also

By default, properties of the controls on the ActiveForm do not appear when the form is opened in a development environment such as Visual Basic. These properties can be published so that they can be modified in a development environment.

To publish a property:

1.	Select Edit|Add to interface while the implementation unit is open in the editor.

2.	Select Property/Method from the Procedure type drop-down list.

3.	Enter the declaration for the property that will expose the control's property. For example, for the Caption property of a button control you could type,

property MyButtonCaption: WideSt ring;

4.	Choose OK.

This adds get and set methods to the implementation unit.

5.	Add code to the methods to get and set the controls property. For example, with the button control above, the code would look as follows:

function TActiveFormX.Get_MyButtonCaption: WideString;

begin

 Result := Button1.Caption;

end;

procedure TActiveFormX.Set_MyButtonCaption(const Value: WideString);

begin

  Button1.Caption := Value;

end;

Since an ActiveForm is an ActiveX control, there is a type library associated with it, and you can additional properties and methods to the ActiveForm. For information on how to do this, see "Creating ActiveX controls."


Deploying the ActiveForm

Section topics 	See also

Once you have finished designing the ActiveForm, you can deploy it to your Web server. In order to deploy the ActiveForm project, Web deployment options must be set appropriately, and the project must be compiled. For more information on the options in this dialog, see "ActiveX Web Deployment."


See also

TActiveForm

ActiveX Web Deployment

Creating ActiveX controls

ActiveX components


ActiveX Web Deployment

Section topics 	See also

Once you have finished creating an ActiveForm or ActiveX control, you can deploy it to your Web server.

Deploying ActiveForms and ActiveX controls

To deploy the ActiveForm or ActiveX control project, Web deployment options must be set appropriately, and the project must be compiled. For more information on the options in this dialog, see "Setting Web Deployment Options."

To deploy the control:

1.	Select Project|Web Deployment Options to open the Web Deployment Options dialog box.

2.	On the Project page, set the Target Dir to the location as a path of the ActiveForm .DLL on the Web server. This can be a local path name or a UNC path.

3.	Set the Target URL to the location as a Uniform Resource Locators (URL) of the ActiveForm DLL (without the file name) on your Web Server.

4.	Set the HTML Dir to the location as a path of the HTML file that contains a reference to the ActiveForm. This can be a standard path name or a UNC path.

5.	Choose OK.

6.	Choose Project|Web Deploy.

This creates the .OCX file that contains the ActiveForm or ActiveX control.

The .OCX file is placed in the Target Dir of step 3. The HTML file has the same name as the project file but with a .HTM extension, and it is created in the HTML Dir of step 5. The HTML file contains a URL reference to the OCX at the location specified in step 4.

7.	Invoke your Web browser and view the created HTML page.

When this HTML page is viewed in the Web browser, your form or control is displayed and runs as an embedded application within the browser.


Setting Web Deployment Options

Section topics 	See also

The settings in the Web Deployment Options dialog box affect only the current open ActiveX project. To display the Web Deployment Options dialog box select Project|Web Deployment Options. The Web Deployment Options dialog box contains a check box labeled Default. Checking this control saves the current settings from the Project, Packages, Additional Files and Code Signing pages of the Web Deployment Options dialog box as the default options.

To restore Delphi's original default settings, delete or rename the DEFPROJ.DOF file.

CAB file compression

Web deployment options include settings to enable you to deploy the .OCX and any packages and additional files as cabinet files. A cabinet is a single file, usually with a .CAB file extension, that stores compressed files in a file library. Cabinet compression can dramatically decrease download time (up to 70%) of a file. During installation, the browser decompresses the files stored in a cabinet and copies them to the user's system. Each file that you deploy can be .CAB file compressed.

Code signing

Web deployment options include settings to enable you to code sign the .OCX and any of the any packages and additional files. Code signing allows the user of the control to determine with certainty who wrote the control and that the code has not been modified since it was signed. Each file that you deploy can be code signed. Signing data does not alter it. Code signing generates a digital signature that is added to the file.

Note:	Delphi does not provide the credentials and private key required for code signing. Information on how to obtain these files is available from Microsoft.

Packages

When deploying your ActiveForm or ActiveX control, you have the option of specifying deployment options for each required package file as part of your deployment. These packages can each be code signed and be put into .CAB files. Packages that ship with Delphi are already code signed with the Borland signature.

.INF file

If the ActiveForm or ActiveX control is deployed with any packages and/or additional files, an .INF file is automatically created. This file specifies various files that need to be downloaded and setup for the OCX to run. The syntax of the .INF file allows URLs pointing to packages or additional files to download.


Option combinations

Section topics 	See also

The following table summarizes what results from choosing different combinations of package, CAB file compression and code signing Web deployment options.

Packages and/or additional files	CAB file compression	Code signing	Result:
No	No	No	An .OCX file.
No	No	Yes	An .OCX file.
No	Yes	No	A CAB file containing an .OCX file.
No	Yes	Yes	A CAB file containing an .OCX file.
Yes	No	No	An .INF file, an .OCX file, and any additional files and packages.
Yes	No	Yes	A .CAB file containing an .INF file, an .OCX file, and any additional files and packages.
Yes	Yes	No	An .INF file, a .CAB file containing an .OCX file, and a CAB file each for any additional files and packages.
Yes	Yes	Yes	A .CAB file containing an .INF file, a .CAB file containing an .OCX file, and a CAB file each for any additional files and packages.


Project page

Section topics 	See also

The project page enables you to specify the location of files and the URL, and set other deployment options for the project. The options on the project page apply to the .OCX file or .CAB file containing the OCX and become the default options for any packages and additional files deployed with the project..

Directories and URLs	Meaning
Target Dir	Location as a path of the .OCX file on the Web server. This can be a standard path name or a UNC path.
Target URL	Location as a Uniform Resource Locators (URL) of the path for the .OCX file on the Web Server.
HTML Dir	Location as a path of the HTML file that contains a reference to the OCX. This can be a standard path name or a UNC path.
Note:	These are paths only, not complete file names.

General Options	Meaning
Use CAB file compression	Compress the OCX, and required packages and additional files, unless specified otherwise.
Include file version number	Include version number contained in Project|Options VersionInfo.
Auto increment release number	Automatically increment the projects release number contained in Project|Options VersionInfo.
Code sign project	Code sign the .OCX or .CAB file containing the OCX, and unless specified otherwise, any DLLs or additional files to deploy.
Deploy required packages	If checked, any required packages will be deployed with the project.
Deploy additional files	If checked, any additional files specified on the Addition Files page will be deployed with the project.


Packages page

Section topics 	See also

The packages page enables you to specify how packages used by the project will be deployed. Each package used by the project can specify individual settings.

Packages used by this project

To modify the settings for a particular package, select the package in the "Packages used by project" list box, and modify the settings as needed.

CAB options

Option	Meaning
Compress in a separate CAB	Create a separate .CAB file for the package. This is the default.
Compress in a project CAB	Include the package in the project .CAB file.
Output options

The output options allow you to specify if the package has version info and if it is code signed.

Option	Meaning
Use file VersionInfo	This option takes the version info from the resource in the package file and enters it into the.INF file for the project.
Code sign file	Code sign the package or .CAB file containing the package.
Directory and URL options

Option	Meaning
Target URL (or leave blank to assume file exists on target machines)	Location as a Uniform Resource Locators (URL) of the package on your Web Server. If left blank, the Web browser will assume the file exists on the target machine. If the package is not found on the target machine, the download of the OCX will fail.
Target directory (or leave blank to assume file exists on server)	Location as a path of the package on the Web server. This can be a standard path name or a UNC path. Leave blank to assume the file exists and ensure that it won't be overwritten.


Additional Files page

Section topics 	See also

The Additional Files page enables you to specify additional files, such as .DLL files, .INI files, resources, etc., to be deployed with the ActiveX control or ActiveForm.

To add a file to be deploy, click on the Add button. This brings up an dialog from which you can browse and select a file. Each file you add is added to the "Files associated with project" list box.

Files associated with project

To modify the settings for a particular file, select the file in the "Files associated with project" list box, and modify the settings as needed.

CAB options

Option	Meaning
Compress in a separate CAB	Create a separate .CAB file for the file. This is the default.
Compress in a project CAB	Include the file in the project .CAB file.
Output options

Option	Meaning
Use file VersionInfo	If the file contains VersonInfo resources, takes the version info from the resource in the file and enters it into the.INF file for the project.
Code sign file	Code sign the file or .CAB file containing the file.
Directory and URL options

Option	Meaning
Target URL (or leave blank to assume file exists on target machines)	Location as a Uniform Resource Locators (URL) of the file on your Web Server. If left blank, the Web browser will assume the file exists on the target machine. If the file is not found on the target machine, the download of the OCX will fail.
Target directory (or leave blank to assume file exists on server)	Location as a path of the file on the Web server. This can be a standard path name or a UNC path. Leave blank to assume the file exists and ensure that it won't be overwritten.


Code Signing page

Section topics 	See also

The Code Signing page contains settings used when creating a digital signature. To create a digital signature, a hash value (also known as a message digest) is first created using the selected Cryptographic digest algorithm. This hash value is then signed, using the Private key.

The private key and credentials certificate files can be obtained by contacting Microsoft.

Required Information

Option	Meaning
Credentials file	To obtain this file, contact Microsoft.
Private key	The private key is known only to its owner and is used to generate the signature. To obtain this file, contact Microsoft
Optional Information

Option	Meaning
Name of this application	Application name as you would like it to appear in the digital certificate displayed by the Web browser.
Application or company URL	An URL that provides a link to a web page for the product or company.
Cryptographic digest algorithm

Choose one of the following cryptographic digest algorithms. MD5 is the most commonly used and the default. You may want to select one algorithm over the other if your browser does not support the other.

algorithm	Meaning
MD5	The MD5 hashing algorithm was developed by RSA Data Security, Inc. This algorithm generates 128-bit hash value.
SHA 1	The SHA hashing algorithm was developed by the National Institute of Standards and Technology (NIST) and by the National Security Agency (NSA). This algorithm generates a 160-bit hash value.


See also

TActiveXControl

TActiveForm

Setting Web Deployment Options

Creating property pages


Creating property pages

Section topics

Property pages provide a user-friendly way to view and edit the properties of ActiveX controls in a development environment such as Delphi or Visual Basic. A property page dialog allows you to group many properties for a control together to be edited at once, or provide a dialog box user-interface for more complex properties.

The following property page enables the user to edit the EditMask property of a mask edit control. The dialog provides a list of sample masks that the user can choose from and a place to test the mask before applying it to the control.

When the property page is in design mode, the OK, Cancel and Apply buttons and the tab are not displayed. These are automatically added by the property page dialog when the properties of an ActiveX control are edited in a development environment such as Borland C++ Builder, Delphi, Visual Basic, Optima++, IntraBuilder, Paradox for Windows or PowerBuilder.

Each property page that you design and connect to a control appears as a new tab on the property page dialog of the control. Individual properties may also appear in the object inspector of the development environment.

Creating a property page is very similar to creating a form. You can add controls like edit boxes that correspond to the properties of the ActiveX control. You can add events and other controls just like you would to a form. For more information on form design, see the User's Guide.

In general, to create a new property page and connect it to an ActiveX control:

1.	Create the new property page.

2.	Associate a control's properties with the property page.

3.	Connect the property page to the ActiveX control.


Creating a property page

Section topics

To create a new property page:

1.	Choose File|New to open the New Items dialog box.

2.	Choose the ActiveX page which opens the ActiveX page.

3.	Select the Property Page icon and Choose OK.

The property page appears.


Adding controls to the property page

Section topics

Each property of the control to be edited by the user via the property page needs a control that the user can use to update the property. For example, a Caption property can be updated with an Edit control. For more information on choosing the right control for a property, see the User's Guide.

With the Mask Edit property page described above, the EditMask property of the Mask Edit ActiveX control is associated with an edit box on the property page.

Mask Edit property page in design mode:

The List Box allows the user to select from a list of sample masks. The Mask Edit control enables the user to test the mask before applying it to the ActiveX control. Add controls to the property page the same as you would to a form.


Associating properties with a property page

Section topics

Once you have added all the controls you need to the property page you must associate each control with its corresponding property. When a property page is created, two methods are added to the property page in the implementation unit of the property page. These are UpdateObject and UpdatePropertyPage.

The following steps are illustrated using the MaskEdit property page from above.

Updating the object

Add code to UpdateObject to update the property when the user edits the properties control on the property page. The following code updates the EditMask property of the MaskEdit ActiveX control from the InputMask edit box of the property page:

procedure TPropertyPage1.UpdateObject;

begin

 { Update OleObject from your controls }

 OleObject.EditMask := InputMask.Text;

end;

Updating the property page

Add code to UpdatePropertyPage to update the property's control on the property page when the property is changed in some other way. The following code updates the InputMask edit box from the EditMask property:

procedure TPropertyPage1.UpdatePropertyPage;

begin

 { Update your controls from OleObject }

 InputMask.Text := OleObject.EditMask;

end;


Connecting a property page to an ActiveX control

Section topics

To connect the property page to the ActiveX control:

1.	Add DefinePropertyPage with the GUID constant of the property page as the parameter to the DefinePropertyPages method implementation in the controls implementation unit. For example:

procedure TButtonX.DefinePropertyPages(DefinePropertyPage: TDefinePropertyPage);

begin

  DefinePropertyPage(Class_PropertyPage1);

end;

The GUID constant Class_PropertyPage1 of the property page can be found in the property pages unit.

1. 	Add the property page unit to the uses clause of the controls implementation unit.


Creating OLE Automation servers

Section topics 	See also

These topics describe OLE Automation and how to create Automation servers. You can create and add Automation objects to an ActiveX server project using the Automation Object wizard.

 OLE Automation

OLE Automation is the standard mechanism through which OLE enables an object to define and provide access to its properties, methods, and types. An OLE Automation object can be controlled programmatically by other objects and applications that support OLE Automation. Automation objects expose their capabilities through type libraries. OLE Automation involves a controller application that supports OLE Automation, such as Delphi, Visual Basic and Delphi applications, and an Automation server, which can be an application or library.

The objects and controls are contained in an OLE Automation server, which can be either a library or executable. There are two kinds of Automation Servers: in-process and out-of-process. Remote servers build on in-process and out-of-process servers.

In-process servers

An in-process server is a library (DLL) that runs in the same process space as the client. An automation object that is a control is also a library. It usually has, but is not required to have, an .OCX extension.

A library can implement both automation objects and controls.  Controls are simply Automation objects with events that can bind to a container's event sink.  Controls are usually visual whereas automation objects are not. Controls have additional rules and properties that automation objects do not have.

An in-process server can make quicker function calls since it is within the same process and need not use marshalling. For more information on marshaling, see "OLE and ActiveX Overview."

Out-of-process servers

An out-of-process server is an executable, and runs in its own process space.  ActiveX controls can not be out-of-process.  Since executables run in their own process space, the client must use marshalling to use the automation object. The marshalling is accomplished using a dispatch interface.

Remote Servers

A remote server is an out-of-process server that communicates across machine boundaries. With the current version of DCOM, the automation object has to be an executable, but with future versions of DCOM, libraries will also be allowed.  With OLEnterprise you can use libraries or executables for your automation server.

Interfaces

An OLE Automation object's properties, methods, and types can be exposed through IDispatch, the OLE Automation interface. OLE Automation can also be accomplished through a dual interface. For more information, see "OLE and ActiveX Overview."


OLE Automation compatible types

Section topics 	See also

Function result and parameter types of the methods declared in dual and dispatch interfaces must be OLE Automation-compatible types. The following types are OLE Automation-compatible:

The predefined types Smallint, Integer, Single, Double, Currency, TDateTime, WideString, IDispatch, WordBool, OleVariant, SCode, TColor, Byte, TSafeArray and IUnknown.

Enumeration types defined in a type library. OLE Automation-compatible enumeration types are stored as 16-bit values and are treated as values of type Smallint for purposes of parameter passing.

Interface types defined in a type library that are OLE Automation safe, that is, derived from IDispatch and containing only OLE Automation compatible types.

Dispinterface types defined in a type library.

IFont, IStrings, and IPicture. Helper objects must be instantiated to map

an IFont to a TFont,

an IStrings to a TStrings,

an IPicture to a TPicture.

The ActiveX control and ActiveForm wizards create these helper objects automatically when needed.


Overview of Automation server creation

Section topics 	See also

To create a new automation server:

1.	Create a project, or open an existing project, to which you wish to add automation objects.

2.	Use the Automation Object wizard to create automation objects and add to the project.

3.	Add properties, methods, and types to the type library.

4.	Implement your properties and methods.

5.	Compile and register the automation server project.

6.	Test and debug the server with all potential target applications.

Automation objects use properties and methods to implement their behavior and use type libraries to expose their capabilities to applications and other objects.

Type library

The type library contains the type definitions for the object and is automatically created by the Automation Object wizard. This type information provides a way for host applications to find out what the object can do. Type library information is stored in a .TLB file and a .PAS file containing the Pascal translations. The .PAS file is automatically generated, so any changes you make will be lost the next time it is saved. When you build the Automation server, the type library information is automatically compiled into the application as a resource.

Properties and methods

Properties and methods enable the object to be queried and controlled by other applications and objects.

A property is an attribute, such as a name or zip code.

A method (procedure or function) is a request to the object to perform some action.


Creating an Automation server

Section topics 	See also

The Automation Object wizard helps you create new OLE Automation objects that you add to your Automation server.

Creating the Automation server project

When you create a new Automation object, you add it to an OLE Automation server, which is either a new or existing OCX or EXE project.

To create a new Automation server library project:

1.	Choose File|New to open the New Items dialog box.

2.	Choose the ActiveX tab to open the ActiveX page.

3.	Select the ActiveX Library icon and choose OK.

To create a new Automation server executable project choose File|New Application.

Both sets of steps above creates and opens a new project that will become your Automation server project. Automation objects and ActiveX controls can now be added to the project.

Creating the automation object

Use the Automation Object wizard to create a new Automation Object, as you need only specify two things:

The name of the new object

The instancing type

The wizard performs the following tasks for you:

Creates a new unit

Creates a new type library (.TLB) file to which you add properties, methods, and types

Defines a class that descends from TAutoObject

Defines an IDispatch interface for the class

To create a new Automation object:

1.	Choose File|New to open the New Items dialog box.

2.	Choose the ActiveX tab to open the ActiveX page.

3.	Select the Automation Object icon and choose OK.

This brings up the Automation Object wizard dialog box.

4.	Enter a name for the object on the first line of the dialog box.

5.	Select Multiple Instance, Single Instance, or Internal from the Instancing drop down list. (See the next section for more information.)

6.	Choose OK.

The Type Library Editor appears, ready for you to add properties, methods, and types for the Automation object to expose.

Instancing

When you create a new Automation object, it can be either multiple instance, single instance or internal.

Instancing	Meaning
Multiple	Specifies that multiple applications can connect to the object.
Single	Specifies that once an application has connected to the object, it is removed from public view so that no other applications can connect to it. This option is commonly used for single document interface (SDI) applications
Internal	The object can only be created internally. An external application cannot create an instance of the object directly. For example, a word processor application may have a document object that can only be created by calling a method of the application that will create the document object.
Adding properties, methods, and enumeration types

When you use the Automation Object wizard it creates a new .TLB file and opens it in the Type Library Editor for you to add properties, methods, and types for the Automation object to expose to applications and objects.

Use the Type Library editor to add properties, methods, and enumeration types for the Automation object. With the Type Library Editor you may add and edit interfaces, enumeration types, properties and methods. "Working with type libraries."

Enumeration types

An enumeration is a named group of symbolic integral constants. Like C language enumerators, enumerator names must be unique but the enumerator values need not be.

To add an enumeration type to the object:

1.	With the appropriate interface selected, click the enumeration icon on the toolbar.

An enumeration is added to the object list pane.

2.	Type a name for the enumeration and press Enter.

3.	With the new enumeration selected in the object list pane, tab to the Attributes page. Enter and modify attributes such as the Name, Help String, Help Context, and Version. You should not change the GUID value. For a description of these values, see "Working with type libraries."

4.	Tab to the members page, which will be blank. Add values to the enumeration by typing directly on the members page. For example you could type the following declarations on the members page of the DragMode enum used by a button control:

dmManual = 0;

helpstring 'dmManual';

dmAutomatic = 1;

helpstring 'dmAutomatic';

Properties

A property is a member function that sets or returns information about the state of the object, such as color or font. For example, a button control might have a property declared as follows:

property Caption: WideString;

To add a property to the object:

1.	With the appropriate interface selected, click the Property icon on the toolbar.

A property is added to the object list pane prompting you to add a name.

2.	Type a name for the property and press Enter.

3.	In the Declaration field of the Attributes page for the property, replace Integer with the correct type for the property. See "Working with type libraries" for possible values. For example, you could type,

property MyProperty: WideString;

4.	The new property contains other default attributes and flags on its attributes page that you can modify to suit the property.

Get and set methods are added to the objects implementation unit.

5.	Add code to the get and set methods to get and set the object's property.

Methods

A method can be a procedure or function.

To add a new method to the object:

1.	With the appropriate interface selected, click the Method icon on the toolbar.

A method is added to the object list pane.

2.	You are prompted to add a name. Type a name for the property and press Enter.

The new method contains default attributes in its attributes page which can be modified to suit the method. For a description of these attributes, see "Working with type libraries."

Properties and methods can also be added by typing directly on the members page. For example you could type the following property declarations into the members page of an interface:

procedure Click; dispid 1;

property Cancel: WordBool; dispid 2;

property Caption: WideString; dispid 3;

property BorderStyle: BorderStyle; dispid 4;

After the members have been added to an interface member page, the members appear as separate items in the object list pane each with its own attributes page where you can modify each new member's attributes.

You can also add properties and methods types to a type library by selecting Edit|Add to Interface.

Registering an in-process server

Registering an in-process server adds the automation server to the Windows registry makes the server available to other applications such as Visual Basic, Delphi, and C++Builder applications.

To register the Automation server, choose Run|Register ActiveX Server.

When you wish to remove the Automation server from your system, it is recommended that you first remove its entries from the Windows registry.

To unregister the Automation server, choose Run|Unregister ActiveX Server.

Registering an out-of-process server

An out-of-process server will be registered automatically the first time it is run. It will also be registered each time it is run if the /regserver compiler switch has been checked in the Run|Parameters dialog box. Use the /unregserver switch to unregister the server.

Testing and debugging the server

The new OLE Automation server should be tested with all potential target applications.

To debug an executable (out-of-process) server:

1.	Start debugging in the server in the IDE. For information on using the integrated debugger, see the Object Pascal Language Guide.

2.	Outside of Delphi, run the client application.

3.	Set breakpoints in the IDE and wait for them to stop when interacting with the client application.

To debug an in-process server (DLL or OCX), select Run|Parameters and type the client name in the Host Application edit box.

The parameters then apply to the host application. Selecting Run|Run will run the host or client application and allow you to set breakpoints in the server.


See also

OLE and ActiveX Overview

Working with type libraries

ActiveX components

Creating ActiveX controls


Working with type libraries

Section topics

These topics describe how to create and edit type libraries with the Type Library editor. Type libraries are files that include information about data types, interfaces, member functions, and object classes exposed by an ActiveX control or server.

Using type libraries

Including the type library with an application or OCX makes information about the objects it contains available to other applications and programming tools. When you create an ActiveX control or Automation object with the wizards, a type library is created for you as well.

A type library may contain any or all of the following:

Information about data types.

Descriptions of one or more OLE elements, such as an interface or CoClass. Each of these descriptions is commonly referred to as type info.

References to type descriptions from other type libraries.


Deploying type libraries

Section topics

When you create an ActiveX control using the ActiveX control wizard, a type library is automatically compiled into the OCX as a resource when you build your control.

Type libraries can be deployed in any of the following forms:

A resource in an .OCX file. This resource should have the type TYPELIB and an integer ID. If you choose to build type libraries with a resource compiler, it must be declared in the resource (.RC) file as follows:

1 typelib mylib1.tlb

2 typelib mylib2.tlb

There can be multiple type library resources in a OCX. Application developers use the resource compiler to add the .TLB file to their own OCX.

A resource in an .EXE file. The file may contain multiple type libraries.

A stand-alone binary file. The .TLB file output by the Type Library editor is a binary file.


The Type Library editor

Section topics 	See also

The Type Library editor is a tool that enables developers to examine and create type information for ActiveX controls and COM objects. This type information can be provided with an object either stand-alone in a type library (.TLB) file, or integrated into the EXE or OCX as a resource.

This picture shows the type library for an edit control ActiveX open in the Type Library editor:

The main elements of the Type Library editor are:

Toolbar

Object list pane

Error log pane

Attributes pages for all elements in the type library

Uses page for the type library

Members pages for interfaces, COM classes, interface members, and enumeration types

The following sections describe the different elements of the Type Library editor user interface and what type information is provided.


Object list pane

Section topics 	See also

The Object list pane lists all the elements and their type information that are listed in the library. These elements include interfaces, dispinterfaces, CoClasses, interface properties and methods, enumeration types, records, variant records, type definitions, and modules. For more information on these terms, see "OLE and ActiveX Overview." The Object list pane is an expandable hierarchical tree where members of a particular interface appear as children to the interface.

Note:	Only interfaces, dispinterfaces, CoClasses, enumeration types, properties and methods can be added and modified; records, variant records, type definitions and modules contained in a type library can be viewed but not edited in the Type Library editor.

The following color-coded symbols appear to the left of the objects in the Object list (left) pane:

Icon	Meaning
	A type library. This can be expanded to expose the individual type information, including objects and interfaces.
	An interface description
	A dispinterface description
	A CoClass
	A method of the interface
	A property of the interface
	An enumeration
	A record. Read-only.
	A variant record. Read-only.
	A type definition. Read-only.
	A module. Read-only.


Type library information

Section topics 	See also

When a type library is selected in the Object list pane, an Attributes page and Uses page become available.

Attributes page

The Attributes page displays type information about the currently selected library. The following  attributes and flags appear on the Attributes page when a type library

is selected in the main object pane

Attribute	Meaning
Name	The descriptive name of the type library without spaces or punctuation.
GUID	The globally unique 128-bit identifier of the interface.
Version	A particular version of library in cases where multiple versions of the library exist. The Version  is  either a pair of decimal integers separated by a period, or a single decimal integer. The first of the two integers represents the major version number, and the second represents the minor version number of an interface. If a single integer is used, it represents the major version number. Both major and minor version numbers are short unsigned integers in the range between 0 and 65535, inclusive.
LCID	The locale identifier that describes the single national language used for all text strings in the type library and elements. Read only.
Help File	The name of the help file associated with the type library
Help String	A short description  of the library. It is strongly recommended that you provide a help string.
Help Context	The Help context ID of the library.

The following flags specify how the server is to be used by other applications.

Flag	Meaning
None	No flags.
Restricted	Prevents the library from being used by a macro programmer.
Control	Indicates that the library represents a control.
Hidden	Indicates that the library exists but should not be displayed in a user-oriented browser.
Uses page

The Uses page in the editor lists other type libraries referenced by the type library.

The following columns appear on the Uses page when a type library is selected in the main object pane and list type libraries referenced by the selected library.

Column	Meaning
Type Library	Reference to another type library.
GUID	The globally unique 128-bit identifier of the referenced type library.


Attributes for type information

Section topics 	See also

The following attributes appear on the Attributes page when an enumeration, interface, dispinterface, CoClass, record, variant record type definition or module type is selected in the main object pane.

Attribute	Meaning
Name	The name of the type.
GUID	The globally unique 128-bit identifier of the interface.  This is optional.
Help String	A short description of the type. It is strongly recommended that you provide a help string.
Help Context	The Help context ID of the type.
Version	Identifies a particular version of the type in cases where multiple versions of the type exist. The Version  is  either a pair of decimal integers separated by a period, or a single decimal integer. The first of the two integers represents the major version number, and the second represents the minor version number of an interface. If a single integer is used, it represents the major version number. Both major and minor version numbers are short unsigned integers in the range between 0 and 65535, inclusive.
Parent Interface	Interfaces and objects only. The name of the interface that is the base interface for this interface. Only applies to interfaces.


Members page for an enumeration

Section topics 	See also

This page contains the type definition of the enumeration type.  This can also include a help string and a help context for each enumeration element.

The following is a sample entry of an enumeration type for beveling on a panel and includes a help string for each enumeration element. It is strongly recommended that you provide a help string for your enumerations. Applications that use your COM object may depend on a help string.

bvNone = 0;

helpstring 'bvNone';

bvLowered = 1;

helpstring 'bvLowered';

bvRaised = 2;

helpstring 'bvRaised';


Interface and CoClass flags

Section topics 	See also

The following flags appear on the Attributes page when an interface or CoClass is selected in the main object pane.

Flag	Valid for:	Meaning
DispInterface	Interface	The interface describes the methods, and properties for an object that must be accessed through IDispatch.Invoke and cannot be accessed through a v-table.
Can Create	CoClass	Instance can be created with CoCreateInstance.
Application Object	CoClass	Identifies the CoClass as an application object, which is associated with a full EXE application, and indicates that the functions and properties of the CoClass are globally available in this type library.
Licensed	CoClass	Indicates that the CoClass to which it applies is licensed, and must be instantiated using IClassFactory2.
Predefined	CoClass	The client application should automatically create a single instance of the object that has this attribute.
Hidden	All	Indicates that the interface exists but should not be displayed in a user-oriented browser.
Control	CoClass	Identifies a CoClass as an OLE control, from which a container site will derive additional type libraries or CoClasses.
Non-extensible	Interface	Indicates that the IDispatch implementation includes only the properties and methods listed in the interface description.
Dual	Interface	Identifies an interface that exposes properties and methods through IDispatch and directly through a v-table.
Ole Automation	Interface	Indicates that an interface can use only OLE Automation compatible types. This flag is not allowed on dispinterfaces since they are OLE automation compatible by definition.
Aggregatable	CoClass	Indicates that the members of the class can be aggregated.
Replaceable	CoClass	The object supports IConnectionPointWithDefault
Members page for an interface

The Members page defines the property and methods of the interface.  A DispID can also be defined for each member. The following sample below defines properties contained in an interface.

property Caption: WideString; dispid 1;

property BevelInner: DAXPanelBevel; dispid 2;

property BevelOuter: DAXPanelBevel; dispid 3;

property BevelWidth: Integer; dispid 4;

property BorderWidth: Integer; dispid 5;

property WindowBorder: DAXBorderStyle; dispid 6;

property Font: Font; dispid 7;

The following sample defines methods contained in an interface.

procedure OnClick; dispid 1;

procedure OnEnter; dispid 2;

procedure OnExit; dispid 3;

procedure OnDblClick; dispid 4;

procedure OnKeyPress(var Key: Byte); dispid 5;

In the Microsoft Interface Definition Language (IDL) used to build a TLB from a script file, ElemDescs  are to used describe information needed for transferring a record element, parameter, or function return value between processes. These include flags such as PARAMFLAG_NONE, PARAMFLAG_FOUT, PARAMFLAG_FRETVAL, PARAMFLAG_FLCID, PARAMFLAG_FOPT, and PARAMFLAG_FHASDEFAULT.

If the parameter type is a pointer type, the Type Library editor will usually translate that type into a var parameter.  When the type library is saved, the variable parameter's associated ElemDesc's PARAMFLAG flags will be marked (PARAMFLAG_FIN or PARAMFLAG_FOUT).  Often, ElemDesc PARAMFLAG flags are not marked (PARAMFLAG_FIN or PARAMFLAG_FOUT) when the type is preceded with a pointer, or in the case of dispinterfaces, PARAMFLAG flags are not typically used.  In these cases you may see a comment next to the variable identifier like {none}, {lcid}, {optional), {default <value>}, or {in}.  These comments are automatically added by the Type Library editor so that the PARAMFLAG flags are correctly marked when the type library is saved.

PARAMFLAG_LCID indicates that the parameter is a locale ID. Only one parameter can have this attribute. The parameter must have the in attribute and not the out attribute, and its type must be long. The lcid attribute is not allowed on dispinterfaces.

Members page for a CoClass

The following columns appear on the Members page when a CoClass is selected and lists the members of the object.

Column	Meaning
Interface	Name of an interface that the CoClass will implement.
GUID	Identifies the GUID of the member interface of the object.
Source	Indicates that the member is a source of events.
Default	Indicates that the interface or dispinterface represents the default programmability interface. A CoClass can have two default members at most. One represents the source interface or dispinterface, and the other represents the sink interface or dispinterface.
Restricted	Prevents the item from being used by a programmer. A member of an interface cannot have both restricted and default attributes.
default v-table	Enables an object to have two different source interfaces.

New interfaces can be added to the CoClass from a context menu that appears when you right-click in this window. You can choose an interface from this menu to add to the CoClass. The list includes interfaces that are defined in the current type library and defined in any type libraries that the current type library references.


Interface members

Section topics 	See also

Attributes page

The following attributes and flags appear on the Attributes page when a property or method of an interface is selected in the main object pane.

Attribute	Meaning
Declaration	The declaration of the property or method.
ID	Dispatch ID
Help String	Identifies the documentation string of the property or method. This usually contains a descriptive title of item.
Help Context	Identifies the Help context ID of the property or method.
Property Type	Indicates whether the property is Read/Write, Read-only, Write-only, or By Reference Only
Flag	Property only	Identifier	Meaning
By Reference	Yes	byref	Passes the property by reference.
Restricted	No	restricted	Prevents the property or method from being used by a programmer.
Source	No	source	Indicates that the member returns an object or VARIANT that is a source of events.
Bindable	Yes	bindable	Indicates that the property supports data binding.
Request Edit	Yes	requestedit	Indicates that the property supports the OnRequestEdit notification.
Display Bindable	Yes	displaybind	Indicates a property that should be displayed to the user as bindable.
Default Bindable	Yes	defaultbind	Indicates the single, bindable property that best represents the object. Properties that have the defaultbind attribute must also have the bindable attribute. Cannot be specified on more than one property in a dispinterface.
Hidden	No	hidden	Indicates that the property  exists but should not be displayed in a user-oriented browser.
Default Collection Element	Yes	defaultcollelem	Allows for optimization of code in Visual Basic.
UI Default	No	uidefault	Indicates that the type information member is the default member for display in the user interface.
Non Browsable	Yes	nonbrowsable	Indicates that the property appears in an object browser (which does not show property values), but does not appear in a properties browser which does show property values).
Replaceable	No	replaceable	The object supports IConnectionPointWithDefault.
Immediate Bindable	Yes	immediatebind	Allows individual bindable properties on a form to specify this behavior. When this bit is set, all changes will be notified.  The bindable and request edit attribute bits need to be set for this new bit to have an effect.


Error log pane

Section topics 	See also

When loading a type library in the Type Library editor, translation errors may occur because the OLE types do not map directly to Pascal types. When saving or loading a type library, syntax, translation errors and warnings are listed in the Error log pane.

For example, Pointer to Pointer to Integer will map to variable Pointer to Integer, but because Pascal requires all parameters to be named types, this would be an illegal type. To avoid creating an illegal type, Pointer to Pointer to integer is translated to variable Pointer.  Because this is not a perfect match, when you save the type library, information about the original type will be lost.  The Error Log lists these problems allowing you the correct or note these warnings.


Valid types

Section topics 	See also

The Type Library editor can edit the sub-set of types representable in a type library that are most useful in Delphi. Some types that are more appropriate for other languages, such as C and C++, are not editable in the Type Library editor.

The following types are valid in a type library. The OLE Automation compatible column specifies whether the type can be used by an interface that has its OLE Automation or DispInterface flag checked.

Pascal type	OLE variant type	OLE Automation compatible	Description
Smallint	VT_I2	Yes	2-byte signed integer
Integer	VT_I4	Yes	4-byte signed integer
Single	VT_R4	Yes	4-byte real
Double	VT_R8	Yes	8-byte real
Currency	VT_CY	Yes	currency
TDateTime	VT_DATE	Yes	date
WideString	VT_BSTR	Yes	binary string
IDispatch	VT_DISPATCH	Yes	pointer to IDispatch interface
SCODE	VT_ERROR	Yes	Ole Error Code
WordBool	VT_BOOL	Yes	True = -1, False = 0
OleVariant	VT_VARIANT	Yes	Ole Variant
IUnknown	VT_UNKNOWN	Yes	pointer to IUnknown interface
Shortint	VT_I1	No	1 byte signed integer
Byte	VT_UI1	Yes	1 byte unsigned integer
Word	VT_UI2	No*	2 byte unsigned integer
UINT	VT_UI4	No*	4 byte unsigned integer
Comp	VT_I8	No	8 byte signed real
Largeuint	VT_UI8	No	8 byte unsigned real
SYSINT	VT_INT	No*	system dependent integer (Win32=Integer)
SYSUINT	VT_UINT	No*	system dependent unsigned integer
HResult	VT_HRESULT	No	32 bit error code
Pointer	VT_PTR -> VT_VOID	No	untyped pointer
TSafeArray	VT_SAFEARRAY	No	OLE Safe Array
PChar	VT_LPSTR	No	pointer to Char
PWideChar	VT_LPWSTR	No	pointer to WideChar
Word, UINT, SYSINT, and SYSUINT may be OLE Automation compatible with some applications.

Note:	Byte (VT_UI1) is automation compatible, but is not allowed in a Variant or OleVariant since many automation servers do not handle this value correctly.

Besides these Pascal types, any interfaces and types defined in the library or defined in referenced libraries can be used in a type library definition.

SafeArrays

In the Type Library editor, a SafeArray must specify its component type. For example in the following code snipit, the SafeArray specifies a component type of Integer.

procedure HighLightLines(Lines: TSafeArray(Integer));

The component type for a SafeArray must an OLE Automation compatible type. In the Delphi type library translation unit, the component type isn't necessary or allowed.


Creating a new type library

Section topics 	See also

To create a new type library:

1.	Choose File|New to open the New Items dialog box.

2.	Choose the ActiveX page which opens the New page.

3.	Select the Type Library icon.

4.	Choose OK.

The Type Library editor opens with a prompt to enter a name for the type library.

5.	Enter a name for the type library and press Enter.

Interfaces, CoClasses, enum's, properties and methods can be added to the type library.

Adding an interface to the type library

To add an interface:

1.	On the toolbar, click on the interface icon.

An interface is added to the object list pane prompting you to add a name.

2.	Type a name for the interface in the interface and press Enter.

The new interface contains defaults attributes which can be modified as needed. The members page is empty and you can now add properties and methods to suit the purpose of the interface.

Adding properties and methods to the type library

To add interface members to an interface:

1.	With the interface to add the member to selected, on the toolbar, click on either the property or method icon.

An interface member is added to the object list pane prompting you to add a name.

2.	Type a name for the member and press Enter.

The new member contains default attributes in its attributes page which can be modified to suit the member.

Properties and methods can also be added my typing directly into the members page. For example you could type the following property declarations into the members page of an interface:

property AutoSelect: WordBool; dispid 1;

property AutoSize: WordBool; dispid 2;

property BorderStyle: BorderStyle; dispid 3;

After the members have been added to an interface member page, the members appear as separate items in the object list pane each with its own attributes page where you can modify each new member's attributes.

Adding a CoClass to the type library

To add a CoClass to a type library:

1.	On the toolbar, click on the CoClass icon.

A CoClass is added to the object list pane prompting you to add a name.

2.	Type a name for the class and press Enter.

The new class contains default attributes in its attributes page which can be modified to suit the class. The members page is blank. To add members,

3.	Right-Click in the members page for the class.

A context menu appears which contains a list of interfaces from which you can choose. The list includes interfaces that are defined in the current type library and defined in any type libraries that the current type library references.

4.	Double-click on an interface you want the class to implement.

The interface is added to the page with along with its GUID and other attributes.

Adding an enumeration to the type library

To add enumerations to a type library:

1.	On the toolbar, click on the enum icon.

An enum type is added to the object list pane prompting you to add a name.

2.	Type a name for the member and press Enter.

The new enum is empty and contains default attributes in its attributes page which can be modified to suit the enum and an empty members page for the enum will be blank.

Add values to the enum by typing directly into the members page. For example you could type the following declarations into the members page of an enum used by a button control:

mbLeft = 0;

helpstring 'mbLeft';

mbRight = 1;

helpstring 'mbRight';

mbMiddle = 2;

helpstring 'mbMiddle';


Saving and registering type library information

Section topics 	See also

Type library information is stored in a .TLB file which is an OLE compound document file. Delphi also stores type library information in a Pascal file. The Pascal file is a unit that contains the interface, type and class information of the type library. You should not edit this file as any changes you make will be overridden the next time you save the type library.

When you use the wizards to create an ActiveX control, automation object or ActiveForm, a type library is automatically created along with an implementation unit. The implementation unit is automatically updated when the type library is saved or refreshed.

Type library information can be stored from the Type Library editor in a variety of ways:

Save

Refresh

Register

Saving a type library

Saving a type library:

Performs a syntax check.

Saves information out to a .TLB file.

Saves information out to a PAS file.

Notifies the module manager to update the implementation if the type library is associated with an ActiveForm, ActiveX control or Automation object.

To save the type library, choose File|Save.

Refreshing the type library

Refreshing the type library:

Performs a syntax check.

Regenerates the Pascal type library file in memory only. Does not save to disk.

Notifies the module manager to update the implementation if the type library is associated with an ActiveForm, ActiveX control or Automation object.

To refresh the type library choose the Refresh icon on the toolbar.

Note:	If you have renamed or deleted items from the type library, refreshing the implementation may create duplicate entries. In this case, you will need to move your code to the correct entry and delete any duplicates.

Registering the type library

Registering the type library:

Performs a syntax check.

Adds and entry to the Windows Registry for the type library.

To register the type library, choose the Register icon on the toolbar.


See also

OLE and ActiveX Overview